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!
|
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
|
## Credits
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -110,7 +110,7 @@ public final class AntarchyConfigCatalog {
|
|||||||
|| name.startsWith("nightmareSword") || name.startsWith("basiliskDagger")
|
|| name.startsWith("nightmareSword") || name.startsWith("basiliskDagger")
|
||||||
|| name.startsWith("squidzooka") || name.equals("invertProjectilesFromInvertedPlayers")
|
|| name.startsWith("squidzooka") || name.equals("invertProjectilesFromInvertedPlayers")
|
||||||
|| name.startsWith("gravityGun") || name.startsWith("critterCage")
|
|| name.startsWith("gravityGun") || name.startsWith("critterCage")
|
||||||
|| name.startsWith("minersDream") || name.equals("potentNyxiteInvertedDurationSeconds")
|
|| name.equals("potentNyxiteInvertedDurationSeconds")
|
||||||
|| name.equals("corneaEarNightVisionSeconds") || name.startsWith("american")
|
|| name.equals("corneaEarNightVisionSeconds") || name.startsWith("american")
|
||||||
|| name.startsWith("mogglesVision") || name.equals("ductTapeRepairPercentPerUse")
|
|| name.startsWith("mogglesVision") || name.equals("ductTapeRepairPercentPerUse")
|
||||||
|| name.startsWith("fallenKingCrown");
|
|| name.startsWith("fallenKingCrown");
|
||||||
|
|||||||
@@ -163,11 +163,6 @@ public final class AntarchySettings {
|
|||||||
private static double gravityGunMaxHoldDistance = 10.0D;
|
private static double gravityGunMaxHoldDistance = 10.0D;
|
||||||
private static double critterCageMaxCapturableWidth = 1.65D;
|
private static double critterCageMaxCapturableWidth = 1.65D;
|
||||||
private static double critterCageMaxCapturableHeight = 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 lucidAttackRange = 9.0D;
|
||||||
private static double lucidPearlInvertedDurationSeconds = 6.0D;
|
private static double lucidPearlInvertedDurationSeconds = 6.0D;
|
||||||
private static int corneaEarNightVisionSeconds = 15;
|
private static int corneaEarNightVisionSeconds = 15;
|
||||||
@@ -1189,26 +1184,6 @@ public final class AntarchySettings {
|
|||||||
return critterCageMaxCapturableHeight;
|
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) {
|
public static void setDisableInfinityBookPortalCreation(boolean value) {
|
||||||
disableInfinityBookPortalCreation = value;
|
disableInfinityBookPortalCreation = value;
|
||||||
}
|
}
|
||||||
@@ -1731,26 +1706,6 @@ public final class AntarchySettings {
|
|||||||
critterCageMaxCapturableHeight = value;
|
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() {
|
public static double lucidAttackRange() {
|
||||||
return 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> 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> 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> 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_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> 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"));
|
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.AbstractContainerMenu;
|
||||||
import net.minecraft.world.inventory.HopperMenu;
|
import net.minecraft.world.inventory.HopperMenu;
|
||||||
import net.minecraft.world.item.ItemStack;
|
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.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
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) {
|
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);
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||||
return blockEntity instanceof Container container ? container : null;
|
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.capture": "Rechtsklicke mit einer Glasflasche auf eine Reverie, um sie einzufangen.",
|
||||||
"tooltip.antarchy.reverie_bottle.release": "Rechtsklick, um die Reverie freizulassen.",
|
"tooltip.antarchy.reverie_bottle.release": "Rechtsklick, um die Reverie freizulassen.",
|
||||||
"item.antarchy.gravity_gun": "Gravitationskanone",
|
"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.diamond_minecart": "Die Diamantlore",
|
||||||
"item.antarchy.cogwheel_pet": "Zahnrad-Haustier",
|
"item.antarchy.cogwheel_pet": "Zahnrad-Haustier",
|
||||||
"item.antarchy.fire_dragon_pet": "Feuerdrachen-Haustier",
|
"item.antarchy.fire_dragon_pet": "Feuerdrachen-Haustier",
|
||||||
|
|||||||
@@ -632,11 +632,6 @@
|
|||||||
"tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.",
|
"tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.",
|
||||||
"item.antarchy.gravity_gun": "Gravity Gun",
|
"item.antarchy.gravity_gun": "Gravity Gun",
|
||||||
"item.antarchy.portal_gun": "Portal Gun",
|
"item.antarchy.portal_gun": "Portal Gun",
|
||||||
"item.antarchy.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.diamond_minecart": "The Diamond Minecart",
|
||||||
"item.antarchy.cogwheel_pet": "Cogwheel Pet",
|
"item.antarchy.cogwheel_pet": "Cogwheel Pet",
|
||||||
"item.antarchy.fire_dragon_pet": "Fire Dragon Pet",
|
"item.antarchy.fire_dragon_pet": "Fire Dragon Pet",
|
||||||
|
|||||||
@@ -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.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.",
|
"tooltip.antarchy.reverie_bottle.release": "Clic derecho para liberar a la Reverie.",
|
||||||
"item.antarchy.gravity_gun": "Pistola gravitatoria",
|
"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.diamond_minecart": "La vagoneta de diamante",
|
||||||
"item.antarchy.cogwheel_pet": "Mascota engranaje",
|
"item.antarchy.cogwheel_pet": "Mascota engranaje",
|
||||||
"item.antarchy.fire_dragon_pet": "Mascota de dragón de fuego",
|
"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.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.",
|
"tooltip.antarchy.reverie_bottle.release": "Clic droit pour libérer la Reverie.",
|
||||||
"item.antarchy.gravity_gun": "Canon à gravité",
|
"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.diamond_minecart": "Le wagonnet de diamant",
|
||||||
"item.antarchy.cogwheel_pet": "Familier engrenage",
|
"item.antarchy.cogwheel_pet": "Familier engrenage",
|
||||||
"item.antarchy.fire_dragon_pet": "Familier dragon de feu",
|
"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.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.",
|
"tooltip.antarchy.reverie_bottle.release": "Fai clic destro per liberare la Reverie.",
|
||||||
"item.antarchy.gravity_gun": "Cannone gravitazionale",
|
"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.diamond_minecart": "Il carrello di diamante",
|
||||||
"item.antarchy.cogwheel_pet": "Famiglio ingranaggio",
|
"item.antarchy.cogwheel_pet": "Famiglio ingranaggio",
|
||||||
"item.antarchy.fire_dragon_pet": "Famiglio drago di fuoco",
|
"item.antarchy.fire_dragon_pet": "Famiglio drago di fuoco",
|
||||||
|
|||||||
@@ -411,11 +411,6 @@
|
|||||||
"tooltip.antarchy.reverie_bottle.capture": "ガラス瓶を持ってレヴェリーを右クリックすると収納できます。",
|
"tooltip.antarchy.reverie_bottle.capture": "ガラス瓶を持ってレヴェリーを右クリックすると収納できます。",
|
||||||
"tooltip.antarchy.reverie_bottle.release": "右クリックでレヴェリーを解放します。",
|
"tooltip.antarchy.reverie_bottle.release": "右クリックでレヴェリーを解放します。",
|
||||||
"item.antarchy.gravity_gun": "重力銃",
|
"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.diamond_minecart": "ダイヤモンドのトロッコ",
|
||||||
"item.antarchy.cogwheel_pet": "歯車ペット",
|
"item.antarchy.cogwheel_pet": "歯車ペット",
|
||||||
"item.antarchy.fire_dragon_pet": "ファイアドラゴンのペット",
|
"item.antarchy.fire_dragon_pet": "ファイアドラゴンのペット",
|
||||||
|
|||||||
@@ -411,11 +411,6 @@
|
|||||||
"tooltip.antarchy.reverie_bottle.capture": "유리병으로 레버리를 우클릭하여 담습니다.",
|
"tooltip.antarchy.reverie_bottle.capture": "유리병으로 레버리를 우클릭하여 담습니다.",
|
||||||
"tooltip.antarchy.reverie_bottle.release": "우클릭하여 레버리를 풀어줍니다.",
|
"tooltip.antarchy.reverie_bottle.release": "우클릭하여 레버리를 풀어줍니다.",
|
||||||
"item.antarchy.gravity_gun": "중력총",
|
"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.diamond_minecart": "다이아몬드 광산 수레",
|
||||||
"item.antarchy.cogwheel_pet": "톱니바퀴 펫",
|
"item.antarchy.cogwheel_pet": "톱니바퀴 펫",
|
||||||
"item.antarchy.fire_dragon_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.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.",
|
"tooltip.antarchy.reverie_bottle.release": "Clique com o botão direito para soltar a Reverie.",
|
||||||
"item.antarchy.gravity_gun": "Arma Gravitacional",
|
"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.diamond_minecart": "O Carrinho de Diamante",
|
||||||
"item.antarchy.cogwheel_pet": "Mascote de Engrenagem",
|
"item.antarchy.cogwheel_pet": "Mascote de Engrenagem",
|
||||||
"item.antarchy.fire_dragon_pet": "Mascote de Dragão de Fogo",
|
"item.antarchy.fire_dragon_pet": "Mascote de Dragão de Fogo",
|
||||||
|
|||||||
@@ -387,11 +387,6 @@
|
|||||||
"tooltip.antarchy.reverie_bottle.capture": "Нажмите ПКМ стеклянной бутылочкой по грёзе, чтобы поймать её.",
|
"tooltip.antarchy.reverie_bottle.capture": "Нажмите ПКМ стеклянной бутылочкой по грёзе, чтобы поймать её.",
|
||||||
"tooltip.antarchy.reverie_bottle.release": "Нажмите ПКМ, чтобы выпустить грёзу.",
|
"tooltip.antarchy.reverie_bottle.release": "Нажмите ПКМ, чтобы выпустить грёзу.",
|
||||||
"item.antarchy.gravity_gun": "Гравитационная пушка",
|
"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.diamond_minecart": "Алмазная вагонетка",
|
||||||
"item.antarchy.cogwheel_pet": "Шестерёнка-питомец",
|
"item.antarchy.cogwheel_pet": "Шестерёнка-питомец",
|
||||||
"item.antarchy.fire_dragon_pet": "Огненный дракон-питомец",
|
"item.antarchy.fire_dragon_pet": "Огненный дракон-питомец",
|
||||||
|
|||||||
@@ -411,11 +411,6 @@
|
|||||||
"tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右键雷弗里即可将其收纳。",
|
"tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右键雷弗里即可将其收纳。",
|
||||||
"tooltip.antarchy.reverie_bottle.release": "右键释放雷弗里。",
|
"tooltip.antarchy.reverie_bottle.release": "右键释放雷弗里。",
|
||||||
"item.antarchy.gravity_gun": "重力枪",
|
"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.diamond_minecart": "钻石矿车",
|
||||||
"item.antarchy.cogwheel_pet": "齿轮宠物",
|
"item.antarchy.cogwheel_pet": "齿轮宠物",
|
||||||
"item.antarchy.fire_dragon_pet": "火龙宠物",
|
"item.antarchy.fire_dragon_pet": "火龙宠物",
|
||||||
|
|||||||
@@ -411,11 +411,6 @@
|
|||||||
"tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右鍵雷弗裡即可將其收納。",
|
"tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右鍵雷弗裡即可將其收納。",
|
||||||
"tooltip.antarchy.reverie_bottle.release": "右鍵釋放雷弗裡。",
|
"tooltip.antarchy.reverie_bottle.release": "右鍵釋放雷弗裡。",
|
||||||
"item.antarchy.gravity_gun": "重力槍",
|
"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.diamond_minecart": "鑽石礦車",
|
||||||
"item.antarchy.cogwheel_pet": "齒輪寵物",
|
"item.antarchy.cogwheel_pet": "齒輪寵物",
|
||||||
"item.antarchy.fire_dragon_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("elythiaPortalEnabled")
|
||||||
|| name.equals("thoraxisPortalEnabled")
|
|| name.equals("thoraxisPortalEnabled")
|
||||||
|| name.equals("cavarynPortalEnabled")
|
|| 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("hushweedSporeLifetimeSeconds")
|
||||||
|| name.equals("elythiaFireflyParticlesEnabled")
|
|| name.equals("elythiaFireflyParticlesEnabled")
|
||||||
|| name.equals("duplicatorTreeEnabled")
|
|| name.equals("duplicatorTreeEnabled")
|
||||||
@@ -177,7 +185,6 @@ public final class AntarchyConfigCatalog {
|
|||||||
|| name.equals("invertProjectilesFromInvertedPlayers")
|
|| name.equals("invertProjectilesFromInvertedPlayers")
|
||||||
|| name.startsWith("gravityGun")
|
|| name.startsWith("gravityGun")
|
||||||
|| name.startsWith("critterCage")
|
|| name.startsWith("critterCage")
|
||||||
|| name.startsWith("minersDream")
|
|
||||||
|| name.equals("potentNyxiteInvertedDurationSeconds")
|
|| name.equals("potentNyxiteInvertedDurationSeconds")
|
||||||
|| name.equals("corneaEarNightVisionSeconds")
|
|| name.equals("corneaEarNightVisionSeconds")
|
||||||
|| name.startsWith("american")
|
|| name.startsWith("american")
|
||||||
|
|||||||
@@ -167,11 +167,6 @@ public final class AntarchySettings {
|
|||||||
private static double gravityGunMaxHoldDistance = 10.0D;
|
private static double gravityGunMaxHoldDistance = 10.0D;
|
||||||
private static double critterCageMaxCapturableWidth = 1.65D;
|
private static double critterCageMaxCapturableWidth = 1.65D;
|
||||||
private static double critterCageMaxCapturableHeight = 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 lucidAttackRange = 9.0D;
|
||||||
private static double lucidPearlInvertedDurationSeconds = 6.0D;
|
private static double lucidPearlInvertedDurationSeconds = 6.0D;
|
||||||
private static int corneaEarNightVisionSeconds = 15;
|
private static int corneaEarNightVisionSeconds = 15;
|
||||||
@@ -554,6 +549,15 @@ public final class AntarchySettings {
|
|||||||
private static boolean elythiaPortalEnabled = true;
|
private static boolean elythiaPortalEnabled = true;
|
||||||
private static boolean thoraxisPortalEnabled = true;
|
private static boolean thoraxisPortalEnabled = true;
|
||||||
private static boolean cavarynPortalEnabled = 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 boolean elythiaFireflyParticlesEnabled = true;
|
||||||
private static int bloodCrystalArmorShieldRechargeTicks = 600;
|
private static int bloodCrystalArmorShieldRechargeTicks = 600;
|
||||||
private static int bloodCrystalAppleShieldCount = 2;
|
private static int bloodCrystalAppleShieldCount = 2;
|
||||||
@@ -1251,26 +1255,6 @@ public final class AntarchySettings {
|
|||||||
return critterCageMaxCapturableHeight;
|
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) {
|
public static void setDisableInfinityBookPortalCreation(boolean value) {
|
||||||
disableInfinityBookPortalCreation = value;
|
disableInfinityBookPortalCreation = value;
|
||||||
}
|
}
|
||||||
@@ -1327,11 +1311,56 @@ public final class AntarchySettings {
|
|||||||
cavarynPortalEnabled = value;
|
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 permanentPortalsEnabled() { return permanentPortalsEnabled; }
|
||||||
public static boolean permanentPortalsFlintAndSteelEnabled() { return permanentPortalsFlintAndSteelEnabled; }
|
public static boolean permanentPortalsFlintAndSteelEnabled() { return permanentPortalsFlintAndSteelEnabled; }
|
||||||
public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; }
|
public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; }
|
||||||
public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; }
|
public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; }
|
||||||
public static boolean cavarynPortalEnabled() { return cavarynPortalEnabled; }
|
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) {
|
public static void setBrownAntRequiresReagent(boolean value) {
|
||||||
brownAntRequiresReagent = value;
|
brownAntRequiresReagent = value;
|
||||||
@@ -1809,26 +1838,6 @@ public final class AntarchySettings {
|
|||||||
critterCageMaxCapturableHeight = value;
|
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() {
|
public static double lucidAttackRange() {
|
||||||
return 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> 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> 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> 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_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> 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"));
|
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;
|
package com.craisinlord.antarchy.content.block.entity;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import net.minecraft.core.BlockPos;
|
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.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
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 ownerId;
|
||||||
private UUID portalId;
|
private UUID portalId;
|
||||||
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
@@ -26,6 +29,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
|||||||
super(typeSupplier.get(), pos, state);
|
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) {
|
public void configure(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side, BlockPos masterPos) {
|
||||||
this.ownerId = ownerId;
|
this.ownerId = ownerId;
|
||||||
this.portalId = portalId;
|
this.portalId = portalId;
|
||||||
@@ -61,6 +69,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
|||||||
public void updatePair(UUID linkedPortalId, int pairTime) {
|
public void updatePair(UUID linkedPortalId, int pairTime) {
|
||||||
this.linkedPortalId = linkedPortalId;
|
this.linkedPortalId = linkedPortalId;
|
||||||
this.pairTime = pairTime;
|
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();
|
this.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,27 +82,49 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onBroken() {
|
public void onBroken() {
|
||||||
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
|
if (!(this.level instanceof ServerLevel serverLevel)) {
|
||||||
return;
|
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();
|
portal.discard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalBaseBlockEntity blockEntity) {
|
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalBaseBlockEntity blockEntity) {
|
||||||
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
||||||
return;
|
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)
|
if (!(level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master)
|
||||||
|| !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)
|
|| !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)
|
||||||
|| !pos.equals(master.getBasePos())) {
|
|| !master.containsPortalSpot(pos)) {
|
||||||
level.removeBlock(pos, false);
|
level.removeBlock(pos, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal)
|
if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal)
|
||||||
|| portal.isRemoved()
|
|| portal.isRemoved()
|
||||||
|| !pos.equals(portal.getBasePos())
|
|| !portal.containsPortalSpot(pos)
|
||||||
|| blockEntity.ownerId == null
|
|| blockEntity.ownerId == null
|
||||||
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|
||||||
|| blockEntity.side != portal.getPortalSide()) {
|
|| blockEntity.side != portal.getPortalSide()) {
|
||||||
@@ -123,6 +158,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
|||||||
tag.putInt("MasterY", this.masterPos.getY());
|
tag.putInt("MasterY", this.masterPos.getY());
|
||||||
tag.putInt("MasterZ", this.masterPos.getZ());
|
tag.putInt("MasterZ", this.masterPos.getZ());
|
||||||
tag.putInt("PairTime", this.pairTime);
|
tag.putInt("PairTime", this.pairTime);
|
||||||
|
this.portalGun$saveFaceRecords(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.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.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
|
||||||
this.pairTime = tag.getInt("PairTime");
|
this.pairTime = tag.getInt("PairTime");
|
||||||
|
this.portalGun$loadFaceRecords(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.PortalGunPlacement;
|
||||||
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Supplier;
|
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.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.Vec3;
|
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 UUID ownerId;
|
||||||
|
private String ownerIdentity;
|
||||||
|
private UUID gunId;
|
||||||
|
private String channelName;
|
||||||
private UUID portalId;
|
private UUID portalId;
|
||||||
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
private Direction facing = Direction.NORTH;
|
private Direction facing = Direction.NORTH;
|
||||||
private Direction upAxis = Direction.UP;
|
private Direction upAxis = Direction.UP;
|
||||||
private BlockPos basePos = BlockPos.ZERO;
|
private BlockPos basePos = BlockPos.ZERO;
|
||||||
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
|
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
|
||||||
|
private int portalWidth = 1;
|
||||||
|
private int portalHeight = 2;
|
||||||
private Set<BlockPos> compensatedSpots = Set.of();
|
private Set<BlockPos> compensatedSpots = Set.of();
|
||||||
private UUID linkedPortalId;
|
private UUID linkedPortalId;
|
||||||
private int pairTime;
|
private int pairTime;
|
||||||
@@ -48,6 +56,9 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
|
|
||||||
public void configure(
|
public void configure(
|
||||||
UUID ownerId,
|
UUID ownerId,
|
||||||
|
UUID gunId,
|
||||||
|
String ownerIdentity,
|
||||||
|
String channelName,
|
||||||
UUID portalId,
|
UUID portalId,
|
||||||
PortalGunPortalEntity.PortalSide side,
|
PortalGunPortalEntity.PortalSide side,
|
||||||
Direction facing,
|
Direction facing,
|
||||||
@@ -55,20 +66,33 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
BlockPos basePos,
|
BlockPos basePos,
|
||||||
BlockPos[] portalSpots,
|
BlockPos[] portalSpots,
|
||||||
Set<BlockPos> compensatedSpots,
|
Set<BlockPos> compensatedSpots,
|
||||||
int pairTime
|
int pairTime,
|
||||||
|
int portalWidth,
|
||||||
|
int portalHeight
|
||||||
) {
|
) {
|
||||||
this.ownerId = ownerId;
|
this.ownerId = ownerId;
|
||||||
|
this.ownerIdentity = ownerIdentity;
|
||||||
|
this.gunId = gunId;
|
||||||
|
this.channelName = channelName;
|
||||||
this.portalId = portalId;
|
this.portalId = portalId;
|
||||||
this.side = side;
|
this.side = side;
|
||||||
this.facing = facing;
|
this.facing = facing;
|
||||||
this.upAxis = upAxis;
|
this.upAxis = upAxis;
|
||||||
this.basePos = basePos.immutable();
|
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.compensatedSpots = new HashSet<>(compensatedSpots);
|
||||||
this.pairTime = pairTime;
|
this.pairTime = pairTime;
|
||||||
|
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(ownerId, ownerIdentity, gunId, channelName, portalId, this.linkedPortalId, side, facing, upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, pairTime, portalWidth, portalHeight, true));
|
||||||
this.setChanged();
|
this.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Direction, PortalGunPortalFaceRecord> portalGun$getFaceRecords() {
|
||||||
|
return this.faceRecords;
|
||||||
|
}
|
||||||
|
|
||||||
public UUID getPortalId() {
|
public UUID getPortalId() {
|
||||||
return this.portalId;
|
return this.portalId;
|
||||||
}
|
}
|
||||||
@@ -77,6 +101,36 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
return this.ownerId;
|
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() {
|
public PortalGunPortalEntity.PortalSide getSide() {
|
||||||
return this.side;
|
return this.side;
|
||||||
}
|
}
|
||||||
@@ -94,7 +148,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BlockPos[] getPortalSpots() {
|
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() {
|
public Set<BlockPos> getCompensatedSpots() {
|
||||||
@@ -112,6 +175,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
public void updatePair(UUID linkedPortalId, int pairTime) {
|
public void updatePair(UUID linkedPortalId, int pairTime) {
|
||||||
this.linkedPortalId = linkedPortalId;
|
this.linkedPortalId = linkedPortalId;
|
||||||
this.pairTime = pairTime;
|
this.pairTime = pairTime;
|
||||||
|
PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId);
|
||||||
|
if (record != null) {
|
||||||
|
this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime));
|
||||||
|
}
|
||||||
this.setChanged();
|
this.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,18 +187,33 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onBroken() {
|
public void onBroken() {
|
||||||
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
|
if (!(this.level instanceof ServerLevel serverLevel)) {
|
||||||
return;
|
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();
|
portal.discard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalMasterBlockEntity blockEntity) {
|
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalMasterBlockEntity blockEntity) {
|
||||||
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
||||||
return;
|
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)) {
|
if (!blockEntity.hasValidFootprint(level, pos)) {
|
||||||
level.removeBlock(pos, false);
|
level.removeBlock(pos, false);
|
||||||
return;
|
return;
|
||||||
@@ -146,6 +228,12 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
level.removeBlock(pos, false);
|
level.removeBlock(pos, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (blockEntity.gunId == null && portal.getGunId() != null) {
|
||||||
|
blockEntity.adoptGunId(portal.getGunId());
|
||||||
|
} else if (portal.getGunId() == null && blockEntity.gunId != null) {
|
||||||
|
portal.adoptGunId(blockEntity.gunId);
|
||||||
|
}
|
||||||
|
blockEntity.adoptChannelIdentity(portal.getGunId(), portal.getChannelName());
|
||||||
blockEntity.ensureLinkedPortal(level, portal);
|
blockEntity.ensureLinkedPortal(level, portal);
|
||||||
UUID linkedPortalId = portal.getLinkedPortalId();
|
UUID linkedPortalId = portal.getLinkedPortalId();
|
||||||
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|
||||||
@@ -161,16 +249,24 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) {
|
if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(level.getBlockEntity(this.basePos) instanceof PortalGunPortalBaseBlockEntity base)
|
if (!PortalGunPlacement.isRectangularFootprint(this.portalSpots, this.facing, this.upAxis, this.portalWidth, this.portalHeight)) {
|
||||||
|| !base.matches(this.ownerId, this.portalId, this.side)
|
|
||||||
|| !pos.equals(base.getMasterPos())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<BlockPos> supportPositions = new ArrayList<>(2);
|
List<BlockPos> supportPositions = new ArrayList<>(this.portalSpots.length);
|
||||||
for (BlockPos portalSpot : this.portalSpots) {
|
for (int i = 0; i < this.portalSpots.length; i++) {
|
||||||
|
BlockPos portalSpot = this.portalSpots[i];
|
||||||
if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) {
|
if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) {
|
||||||
return false;
|
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()));
|
supportPositions.add(portalSpot.relative(this.facing.getOpposite()));
|
||||||
}
|
}
|
||||||
for (BlockPos supportPos : supportPositions) {
|
for (BlockPos supportPos : supportPositions) {
|
||||||
@@ -186,23 +282,60 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
if (this.portalId == null || this.ownerId == null) {
|
if (this.portalId == null || this.ownerId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (!PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.side, this.portalId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||||
return portal;
|
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"));
|
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_portal"));
|
||||||
if (!(rawType instanceof EntityType<?>)) {
|
if (!(rawType instanceof EntityType<?>)) {
|
||||||
return null;
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
|
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
|
||||||
restored.setUUID(this.portalId);
|
restored.setUUID(this.portalId);
|
||||||
restored.configure(this.ownerId, this.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);
|
restored.restorePair(this.linkedPortalId, this.pairTime);
|
||||||
Vec3 center = placement.center();
|
Vec3 center = placement.center();
|
||||||
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
|
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
|
||||||
level.addFreshEntity(restored);
|
level.addFreshEntity(restored);
|
||||||
PortalGunSavedData.setPortal(level.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;
|
return restored;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +344,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
|
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
|
||||||
level.getServer(),
|
level,
|
||||||
this.ownerId,
|
this.ownerId,
|
||||||
|
this.gunId,
|
||||||
|
this.channelName,
|
||||||
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
|
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
|
||||||
);
|
);
|
||||||
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
|
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
|
||||||
@@ -230,6 +365,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
if (this.ownerId != null) {
|
if (this.ownerId != null) {
|
||||||
tag.putUUID("OwnerId", this.ownerId);
|
tag.putUUID("OwnerId", this.ownerId);
|
||||||
}
|
}
|
||||||
|
if (this.ownerIdentity != null) {
|
||||||
|
tag.putString("OwnerIdentity", this.ownerIdentity);
|
||||||
|
}
|
||||||
|
if (this.gunId != null) {
|
||||||
|
tag.putUUID("GunId", this.gunId);
|
||||||
|
}
|
||||||
|
if (this.channelName != null) {
|
||||||
|
tag.putString("ChannelName", this.channelName);
|
||||||
|
}
|
||||||
if (this.portalId != null) {
|
if (this.portalId != null) {
|
||||||
tag.putUUID("PortalId", this.portalId);
|
tag.putUUID("PortalId", this.portalId);
|
||||||
}
|
}
|
||||||
@@ -243,6 +387,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
tag.putInt("BaseY", this.basePos.getY());
|
tag.putInt("BaseY", this.basePos.getY());
|
||||||
tag.putInt("BaseZ", this.basePos.getZ());
|
tag.putInt("BaseZ", this.basePos.getZ());
|
||||||
tag.putInt("PairTime", this.pairTime);
|
tag.putInt("PairTime", this.pairTime);
|
||||||
|
tag.putInt("PortalWidth", this.portalWidth);
|
||||||
|
tag.putInt("PortalHeight", this.portalHeight);
|
||||||
ListTag spots = new ListTag();
|
ListTag spots = new ListTag();
|
||||||
for (BlockPos portalSpot : this.portalSpots) {
|
for (BlockPos portalSpot : this.portalSpots) {
|
||||||
CompoundTag spot = new CompoundTag();
|
CompoundTag spot = new CompoundTag();
|
||||||
@@ -261,12 +407,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
compensated.add(spot);
|
compensated.add(spot);
|
||||||
}
|
}
|
||||||
tag.put("CompensatedSpots", compensated);
|
tag.put("CompensatedSpots", compensated);
|
||||||
|
this.portalGun$saveFaceRecords(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
super.loadAdditional(tag, registries);
|
super.loadAdditional(tag, registries);
|
||||||
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
|
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
|
||||||
|
this.ownerIdentity = tag.contains("OwnerIdentity") ? tag.getString("OwnerIdentity") : String.valueOf(this.ownerId);
|
||||||
|
this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null;
|
||||||
|
this.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.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
|
||||||
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
|
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
|
||||||
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
@@ -275,10 +425,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
|||||||
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
|
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
|
||||||
this.pairTime = tag.getInt("PairTime");
|
this.pairTime = tag.getInt("PairTime");
|
||||||
ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
|
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);
|
CompoundTag spot = spots.getCompound(i);
|
||||||
this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"));
|
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);
|
ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
|
||||||
Set<BlockPos> compensatedSpots = new HashSet<>();
|
Set<BlockPos> compensatedSpots = new HashSet<>();
|
||||||
for (int i = 0; i < compensated.size(); i++) {
|
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")));
|
compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")));
|
||||||
}
|
}
|
||||||
this.compensatedSpots = compensatedSpots;
|
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
|
@Override
|
||||||
|
|||||||
+5
@@ -20,6 +20,7 @@ import net.minecraft.world.entity.player.Inventory;
|
|||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
import net.minecraft.world.inventory.HopperMenu;
|
import net.minecraft.world.inventory.HopperMenu;
|
||||||
import net.minecraft.world.item.ItemStack;
|
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.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
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) {
|
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);
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||||
return blockEntity instanceof Container container ? container : null;
|
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) {
|
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.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())
|
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 previousEye = new Vec3(this.entity.xo, this.entity.yo + eyeHeight, this.entity.zo);
|
||||||
Vec3 transformed = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolated.subtract(sourcePortal.position())));
|
Vec3 transformedEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolatedEye.subtract(sourcePortal.position())));
|
||||||
Vec3 transformedPrevious = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previous.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 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());
|
Vec3 velocity = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getDeltaMovement());
|
||||||
float yaw = PortalGunTransformUtil.yawFromLook(look);
|
float yaw = PortalGunTransformUtil.yawFromLook(look);
|
||||||
float pitch = PortalGunTransformUtil.pitchFromLook(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.setPos(transformed.x, transformed.y, transformed.z);
|
||||||
this.entity.xo = transformedPrevious.x;
|
this.entity.xo = transformedPrevious.x;
|
||||||
this.entity.yo = transformedPrevious.y;
|
this.entity.yo = transformedPrevious.y;
|
||||||
this.entity.zo = transformedPrevious.z;
|
this.entity.zo = transformedPrevious.z;
|
||||||
this.entity.setYRot(yaw);
|
this.entity.setYRot(yaw);
|
||||||
this.entity.setXRot(pitch);
|
this.entity.setXRot(pitch);
|
||||||
this.entity.yRotO = yaw;
|
this.entity.yRotO = previousYaw;
|
||||||
this.entity.xRotO = pitch;
|
this.entity.xRotO = previousPitch;
|
||||||
this.entity.setDeltaMovement(velocity);
|
this.entity.setDeltaMovement(velocity);
|
||||||
if (this.entity instanceof LivingEntity living) {
|
if (this.entity instanceof LivingEntity living) {
|
||||||
living.setYHeadRot(yaw);
|
living.setYHeadRot(living.getYHeadRot() + yawDelta);
|
||||||
living.setYBodyRot(yaw);
|
living.setYBodyRot(living.yBodyRot + yawDelta);
|
||||||
|
living.yHeadRotO += yawDelta;
|
||||||
|
living.yBodyRotO += yawDelta;
|
||||||
}
|
}
|
||||||
return transformed;
|
return transformed;
|
||||||
}
|
}
|
||||||
@@ -73,13 +86,17 @@ public final class PortalGunEntityTransformationStack {
|
|||||||
float yRotO,
|
float yRotO,
|
||||||
float xRotO,
|
float xRotO,
|
||||||
float yHeadRot,
|
float yHeadRot,
|
||||||
|
float yHeadRotO,
|
||||||
float yBodyRot,
|
float yBodyRot,
|
||||||
|
float yBodyRotO,
|
||||||
Vec3 deltaMovement
|
Vec3 deltaMovement
|
||||||
) {
|
) {
|
||||||
private static EntityTransformation capture(Entity entity) {
|
private static EntityTransformation capture(Entity entity) {
|
||||||
float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot();
|
float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot();
|
||||||
|
float yHeadRotO = entity instanceof LivingEntity living ? living.yHeadRotO : entity.yRotO;
|
||||||
float yBodyRot = entity instanceof LivingEntity living ? living.yBodyRot : entity.getYRot();
|
float 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) {
|
private void restore(Entity entity) {
|
||||||
@@ -94,7 +111,9 @@ public final class PortalGunEntityTransformationStack {
|
|||||||
entity.setDeltaMovement(this.deltaMovement);
|
entity.setDeltaMovement(this.deltaMovement);
|
||||||
if (entity instanceof LivingEntity living) {
|
if (entity instanceof LivingEntity living) {
|
||||||
living.setYHeadRot(this.yHeadRot);
|
living.setYHeadRot(this.yHeadRot);
|
||||||
|
living.yHeadRotO = this.yHeadRotO;
|
||||||
living.setYBodyRot(this.yBodyRot);
|
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 com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.UUID;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
@@ -133,7 +134,8 @@ public final class PortalGunPortalRenderState {
|
|||||||
Matrix4f viewMatrix,
|
Matrix4f viewMatrix,
|
||||||
Matrix4f projectionMatrix,
|
Matrix4f projectionMatrix,
|
||||||
int renderLevel,
|
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 java.util.UUID;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public final class PortalGunRollClientState {
|
public final class PortalGunRollClientState {
|
||||||
private static final 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_DECAY = 0.85F;
|
||||||
private static final float ROLL_EPSILON = 0.05F;
|
private static final float ROLL_EPSILON = 0.05F;
|
||||||
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
|
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
|
||||||
@@ -32,8 +34,11 @@ public final class PortalGunRollClientState {
|
|||||||
if (minecraft.level == null || entity == null) {
|
if (minecraft.level == null || entity == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Entity renderEntity : minecraft.level.entitiesForRendering()) {
|
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass(
|
||||||
if (renderEntity instanceof PortalGunPortalEntity portal && portal.isAlive() && portal.intersectsEntityBounds(entity)) {
|
PortalGunPortalEntity.class,
|
||||||
|
entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
|
||||||
|
Entity::isAlive)) {
|
||||||
|
if (portal.intersectsEntityBounds(entity)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,10 +72,10 @@ public final class PortalGunRollClientState {
|
|||||||
}
|
}
|
||||||
long gameTime = minecraft.level.getGameTime();
|
long gameTime = minecraft.level.getGameTime();
|
||||||
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
|
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
|
||||||
for (Entity entity : minecraft.level.entitiesForRendering()) {
|
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass(
|
||||||
if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) {
|
PortalGunPortalEntity.class,
|
||||||
continue;
|
cameraEntity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
|
||||||
}
|
Entity::isAlive)) {
|
||||||
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
|
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
|
||||||
if (linkedPortal == null || !linkedPortal.isAlive()) {
|
if (linkedPortal == null || !linkedPortal.isAlive()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -81,11 +86,13 @@ public final class PortalGunRollClientState {
|
|||||||
if (!canTeleport(cameraEntity, gameTime)) {
|
if (!canTeleport(cameraEntity, gameTime)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Vec3 crossingProbe = portal.resolveCrossingProbe(cameraEntity);
|
AABB currentBox = cameraEntity.getBoundingBox();
|
||||||
if (crossingProbe == null) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossingProbe, gameTime);
|
applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossing, gameTime);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,26 +105,30 @@ public final class PortalGunRollClientState {
|
|||||||
return cooldownUntil <= gameTime;
|
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 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getLookAngle()).normalize();
|
||||||
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees)).normalize();
|
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees)).normalize();
|
||||||
Vec3 relativeProbe = currentProbe.subtract(sourcePortal.position());
|
Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo);
|
||||||
Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, relativeProbe);
|
PortalGunPortalEntity.PortalTransitTransform transit = sourcePortal.previewTransit(entity, destinationPortal, crossing, resolvedMovement);
|
||||||
Vec3 transformedEntityOffset = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.position().subtract(currentProbe));
|
Vec3 transformedMotion = transit.movement();
|
||||||
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
|
transformedMotion = clampPortalMotion(transformedMotion);
|
||||||
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));
|
|
||||||
}
|
|
||||||
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
|
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
|
||||||
|
Vec3 exitPos = transit.exitPosition();
|
||||||
entity.teleportTo(exitPos.x, exitPos.y, exitPos.z);
|
entity.teleportTo(exitPos.x, exitPos.y, exitPos.z);
|
||||||
entity.setDeltaMovement(transformedMotion);
|
entity.setDeltaMovement(transformedMotion);
|
||||||
|
float verticalMotion = (float) transformedMotion.y;
|
||||||
|
entity.fallDistance = 0.1F * (verticalMotion / -0.1F * verticalMotion / -0.1F);
|
||||||
entity.setYRot(PortalGunTransformUtil.yawFromLook(transformedLook));
|
entity.setYRot(PortalGunTransformUtil.yawFromLook(transformedLook));
|
||||||
entity.setXRot(PortalGunTransformUtil.pitchFromLook(transformedLook));
|
entity.setXRot(PortalGunTransformUtil.pitchFromLook(transformedLook));
|
||||||
|
entity.setPortalCooldown(TELEPORT_COOLDOWN_TICKS);
|
||||||
TELEPORT_COOLDOWNS.put(entity.getUUID(), gameTime + 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.ComputerGame;
|
||||||
import com.craisinlord.antos.api.client.game.ComputerGameRegistry;
|
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.Font;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import java.util.function.IntPredicate;
|
import java.util.function.IntPredicate;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -17,11 +13,11 @@ public final class AntarchyComputerGames {
|
|||||||
private AntarchyComputerGames() {}
|
private AntarchyComputerGames() {}
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
registerIfMissing(game("basilisk", "BASILISK", pos -> {
|
registerIfMissing(game("basilisk", "BASILISK", () -> {
|
||||||
BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> AntarchyGameNetworking.saveBasiliskScore(pos, score));
|
// AntOS 1.2.1 creates game sessions without passing the computer position.
|
||||||
var loaded = new java.util.concurrent.atomic.AtomicBoolean();
|
// Keep Basilisk playable without a position-bound score callback.
|
||||||
AntarchyGameNetworking.requestBasiliskState(pos);
|
BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> {});
|
||||||
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);
|
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);
|
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) {
|
private static ComputerGame game(String path, String title, ProgramFactory factory) {
|
||||||
ResourceLocation id = ResourceLocation.fromNamespaceAndPath("antarchy", path);
|
ResourceLocation id = ResourceLocation.fromNamespaceAndPath("antarchy", path);
|
||||||
ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", path + "_game");
|
ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", path + "_game");
|
||||||
@@ -48,12 +32,12 @@ public final class AntarchyComputerGames {
|
|||||||
@Override public ResourceLocation id() { return id; }
|
@Override public ResourceLocation id() { return id; }
|
||||||
@Override public ResourceLocation diskId() { return disk; }
|
@Override public ResourceLocation diskId() { return disk; }
|
||||||
@Override public String title() { return title; }
|
@Override public String title() { return title; }
|
||||||
@Override public Session create(BlockPos position) { return factory.create(position); }
|
@Override public Session create() { return factory.create(); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@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); }
|
@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 {
|
private static final class GameSession implements ComputerGame.Session {
|
||||||
|
|||||||
+56
-14
@@ -1,11 +1,15 @@
|
|||||||
package com.craisinlord.antarchy.content.client.renderer;
|
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.client.model.PortalGunPortalModel;
|
||||||
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.GraphicsStatus;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
@@ -24,23 +28,61 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
|
|||||||
@Override
|
@Override
|
||||||
public void render(PortalGunPortalEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
public void render(PortalGunPortalEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||||
poseStack.pushPose();
|
poseStack.pushPose();
|
||||||
VertexConsumer consumer = bufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity)));
|
|
||||||
Matrix4f pose = poseStack.last().pose();
|
Matrix4f pose = poseStack.last().pose();
|
||||||
putPortalVertex(consumer, pose, entity, -0.5F, -1.0F, 0.0F, 1.0F, false);
|
float portalScale = entity.getPortalVisualScale(partialTick);
|
||||||
putPortalVertex(consumer, pose, entity, -0.5F, 1.0F, 0.0F, 0.0F, false);
|
float halfWidth = entity.getPortalWidth() * 0.5F;
|
||||||
putPortalVertex(consumer, pose, entity, 0.5F, 1.0F, 1.0F, 0.0F, false);
|
float halfHeight = entity.getPortalHeight() * 0.5F;
|
||||||
putPortalVertex(consumer, pose, entity, 0.5F, -1.0F, 1.0F, 1.0F, false);
|
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)));
|
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, portalScale, -halfWidth - 0.06F, -halfHeight - 0.07F, 0.0F, 1.0F, true);
|
||||||
putPortalVertex(edge, pose, entity, -0.56F, 1.07F, 0.0F, 0.0F, true);
|
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, halfHeight + 0.07F, 0.0F, 0.0F, true);
|
||||||
putPortalVertex(edge, pose, entity, 0.56F, 1.07F, 1.0F, 0.0F, true);
|
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, halfHeight + 0.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, 1.0F, 1.0F, true);
|
||||||
poseStack.popPose();
|
poseStack.popPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void putPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float horizontal, float vertical, float u, float v, boolean channelTint) {
|
private static ResourceLocation portalTexture(PortalGunPortalEntity entity) {
|
||||||
Vec3 position = entity.getWidthVec().normalize().scale(horizontal)
|
String color = entity.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? "blue" : "orange";
|
||||||
.add(entity.getUpVec().normalize().scale(vertical))
|
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));
|
.add(entity.getNormalVec().normalize().scale(0.03125D));
|
||||||
int red = channelTint ? entity.getChannelRed() : 255;
|
int red = channelTint ? entity.getChannelRed() : 255;
|
||||||
int green = channelTint ? entity.getChannelGreen() : 255;
|
int green = channelTint ? entity.getChannelGreen() : 255;
|
||||||
@@ -60,6 +102,6 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable RenderType getRenderType(PortalGunPortalEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
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.Antarchy;
|
||||||
import com.craisinlord.antarchy.content.command.QueenLocateCommand;
|
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.antarchy.content.worldgen.elythia.KingsTreeGrid;
|
||||||
import com.craisinlord.antos.api.archive.ArchiveLocatorRegistry;
|
import com.craisinlord.antos.api.archive.ArchiveLocatorRegistry;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
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. */
|
/** Archive coordinate lookups for royals that live on grids rather than in vanilla structures. */
|
||||||
public final class AntarchyArchiveLocators {
|
public final class AntarchyArchiveLocators {
|
||||||
@@ -24,6 +26,14 @@ public final class AntarchyArchiveLocators {
|
|||||||
Heightmap.Types.WORLD_SURFACE_WG, level, level.getChunkSource().randomState());
|
Heightmap.Types.WORLD_SURFACE_WG, level, level.getChunkSource().randomState());
|
||||||
return new BlockPos(center.getX(), surfaceY, center.getZ());
|
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) {
|
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.entity.glimmer.GlimmerVariant;
|
||||||
import com.craisinlord.antarchy.content.item.GiantFryingPanToolHelper;
|
import com.craisinlord.antarchy.content.item.GiantFryingPanToolHelper;
|
||||||
import com.craisinlord.antarchy.content.item.GlimmerBottleItem;
|
import com.craisinlord.antarchy.content.item.GlimmerBottleItem;
|
||||||
|
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
||||||
import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper;
|
import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper;
|
||||||
import com.craisinlord.antarchy.content.item.ultimate.UltimateGearHelper;
|
import com.craisinlord.antarchy.content.item.ultimate.UltimateGearHelper;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -425,7 +426,7 @@ public final class CreativeTabContents {
|
|||||||
entry("shrink_ray", "combat"),
|
entry("shrink_ray", "combat"),
|
||||||
entry("growth_ray", "combat"),
|
entry("growth_ray", "combat"),
|
||||||
entry("gravity_gun", "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("eye_of_the_storm", "combat"),
|
||||||
entry("water_cannon", "combat"),
|
entry("water_cannon", "combat"),
|
||||||
entry("attitude_adjuster", "combat"),
|
entry("attitude_adjuster", "combat"),
|
||||||
@@ -640,6 +641,14 @@ public final class CreativeTabContents {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ("portal_gun".equals(entry.path())) {
|
||||||
|
Item portalGun = item(entry.path());
|
||||||
|
output.accept(portalGun);
|
||||||
|
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Chell"));
|
||||||
|
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Atlas"));
|
||||||
|
output.accept(PortalGunItem.createGlobalVariant(portalGun, "P-body"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
output.accept(item(entry.path()));
|
output.accept(item(entry.path()));
|
||||||
}
|
}
|
||||||
for (String potionPath : ANTARCHY_POTION_FAMILIES) {
|
for (String potionPath : ANTARCHY_POTION_FAMILIES) {
|
||||||
@@ -669,6 +678,14 @@ public final class CreativeTabContents {
|
|||||||
output.insertAfter(vanillaItem(entry.insertAfterPath()), item(entry.path()));
|
output.insertAfter(vanillaItem(entry.insertAfterPath()), item(entry.path()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ("portal_gun".equals(entry.path())) {
|
||||||
|
Item portalGun = item(entry.path());
|
||||||
|
output.accept(portalGun);
|
||||||
|
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Chell"));
|
||||||
|
output.accept(PortalGunItem.createGlobalVariant(portalGun, "Atlas"));
|
||||||
|
output.accept(PortalGunItem.createGlobalVariant(portalGun, "P-body"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ("combat".equals(tabPath)) {
|
if ("combat".equals(tabPath)) {
|
||||||
if (isUltimateArmor(entry.path())) {
|
if (isUltimateArmor(entry.path())) {
|
||||||
output.accept(UltimateGearHelper.createUltimateArmorStack(item(entry.path()), holders));
|
output.accept(UltimateGearHelper.createUltimateArmorStack(item(entry.path()), holders));
|
||||||
|
|||||||
+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 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 ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/static_item.animation.json");
|
||||||
private static final String MODE_TAG = "antarchy.big_bertha_mode";
|
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_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_X_TAG = "antarchy.big_bertha_spin_direction_x";
|
||||||
private static final String SPIN_DIRECTION_Z_TAG = "antarchy.big_bertha_spin_direction_z";
|
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 NIGHTMARE -> applyNightmareHit(player, target, serverLevel);
|
||||||
case KRAKEN -> applyKrakenHit(player, target, serverLevel);
|
case KRAKEN -> applyKrakenHit(player, target, serverLevel);
|
||||||
case HERCULES -> applyHerculesHit(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) {
|
public boolean tryCycleModeFromInput(Level level, Player player, ItemStack stack) {
|
||||||
if (player.getMainHandItem() != stack) {
|
if (player.getMainHandItem() != stack) {
|
||||||
return false;
|
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"),
|
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"),
|
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"),
|
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");
|
HERCULES(6, "tooltip.antarchy.big_bertha.mode.hercules", "message.antarchy.big_bertha.mode.hercules");
|
||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
@@ -671,7 +679,9 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
|||||||
case KRAKEN -> HERCULES;
|
case KRAKEN -> HERCULES;
|
||||||
case HERCULES -> MOLEVORE;
|
case HERCULES -> MOLEVORE;
|
||||||
case MOLEVORE -> TORETERROR;
|
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.Antarchy;
|
||||||
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
|
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
|
||||||
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
|
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.model.ResourceBackedGeoItemModel;
|
||||||
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
|
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
|
||||||
import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload;
|
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.PortalGunPortalEntity;
|
||||||
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
|
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
|
||||||
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunResetManager;
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@@ -27,8 +32,6 @@ import net.minecraft.network.chat.Component;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.sounds.SoundEvent;
|
|
||||||
import net.minecraft.sounds.SoundEvents;
|
|
||||||
import net.minecraft.sounds.SoundSource;
|
import net.minecraft.sounds.SoundSource;
|
||||||
import net.minecraft.stats.Stats;
|
import net.minecraft.stats.Stats;
|
||||||
import net.minecraft.world.InteractionHand;
|
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.BlockHitResult;
|
||||||
import net.minecraft.world.phys.HitResult;
|
import net.minecraft.world.phys.HitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
import software.bernie.geckolib.animatable.GeoItem;
|
import software.bernie.geckolib.animatable.GeoItem;
|
||||||
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
|
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
|
||||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
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 LAST_SIDE_TAG = "antarchy.portal_gun_last_side";
|
||||||
private static final String MOON_SIDE_TAG = "antarchy.portal_gun_moon_side";
|
private static final String MOON_SIDE_TAG = "antarchy.portal_gun_moon_side";
|
||||||
public static final String GUN_ID_TAG = "antarchy.portal_gun_id";
|
public static final String GUN_ID_TAG = "antarchy.portal_gun_id";
|
||||||
|
public static final String CHANNEL_NAME_TAG = "antarchy.portal_gun_channel";
|
||||||
|
public static final String OWNER_ID_TAG = "antarchy.portal_gun_owner";
|
||||||
|
public static final String OWNER_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 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 TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/models/item/portal_gun.png");
|
||||||
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun.animation.json");
|
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun.animation.json");
|
||||||
@@ -104,81 +117,126 @@ public class PortalGunItem extends Item implements GeoItem {
|
|||||||
}
|
}
|
||||||
ServerLevel serverLevel = (ServerLevel) level;
|
ServerLevel serverLevel = (ServerLevel) level;
|
||||||
if (player.isShiftKeyDown()) {
|
if (player.isShiftKeyDown()) {
|
||||||
PortalGunSavedData.clearAllPortals(serverLevel.getServer(), player.getUUID());
|
if (player instanceof ServerPlayer serverPlayer) {
|
||||||
this.setLastSide(stack, null);
|
this.resetPortals(serverPlayer, stack);
|
||||||
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);
|
|
||||||
return InteractionResultHolder.consume(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);
|
return fired ? InteractionResultHolder.consume(stack) : InteractionResultHolder.fail(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void firePrimary(ServerLevel level, ServerPlayer player, ItemStack stack) {
|
public void firePrimary(ServerLevel level, ServerPlayer player, ItemStack stack, boolean offhand) {
|
||||||
this.firePortal(level, player, stack, PortalGunPortalEntity.PortalSide.BLUE);
|
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.raycast(level, player).getType() == HitResult.Type.MISS) {
|
||||||
if (this.tryArmMoonShot(level, player, stack, side)) {
|
if (this.tryArmMoonShot(level, player, stack, side, offhand)) {
|
||||||
return true;
|
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.triggerFireAnimation(level, player, stack);
|
||||||
this.setLastSide(stack, side);
|
this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName());
|
||||||
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
|
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
|
||||||
player.awardStat(Stats.ITEM_USED.get(this));
|
player.awardStat(Stats.ITEM_USED.get(this));
|
||||||
player.getCooldowns().addCooldown(this, 6);
|
player.getCooldowns().addCooldown(this, 6);
|
||||||
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
|
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
|
||||||
level.playSound(null, 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);
|
this.spawnProjectile(level, player, stack, side);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handlePortalImpact(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos) {
|
public void handlePortalImpact(ServerLevel level, LivingEntity shooter, ItemStack stack, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos, int portalWidth, int portalHeight) {
|
||||||
PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side);
|
UUID portalOwnerId = getPortalOwnerId(stack, shooter.getUUID());
|
||||||
PortalGunPlacement placement = this.findPlacement(level, player, side, hitResult, existing);
|
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) {
|
if (placement == null) {
|
||||||
Antarchy.LOGGER.info("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", player.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation());
|
Antarchy.LOGGER.debug("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", shooter.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation());
|
||||||
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
|
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (existing != null) {
|
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();
|
existing.discard();
|
||||||
}
|
}
|
||||||
if (this.isMoonSideArmed(stack, side)) {
|
if (this.isMoonSideArmed(stack, side)) {
|
||||||
this.setMoonSide(stack, null);
|
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());
|
this.spawnBlackHole(level, player, stack, side, placement.center());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level);
|
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);
|
portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F);
|
||||||
if (!this.placePortalBlocks(level, player.getUUID(), side, portal, placement)) {
|
if (!this.placePortalBlocks(level, portalOwnerId, gunId, portalOwnerIdentity, channelName, 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());
|
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", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
|
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
level.addFreshEntity(portal);
|
level.addFreshEntity(portal);
|
||||||
PortalGunSavedData.setPortal(level.getServer(), player.getUUID(), side, portal.getUUID());
|
PortalGunSavedData.setPortal(level, portalOwnerId, gunId, channelName, 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, portalOwnerId, gunId, channelName, side);
|
||||||
PortalGunPortalEntity other = this.findCounterpart(level, player.getUUID(), side);
|
|
||||||
if (other != null) {
|
if (other != null) {
|
||||||
portal.linkTo(other);
|
portal.linkTo(other);
|
||||||
other.linkTo(portal);
|
other.linkTo(portal);
|
||||||
this.syncPortalBlocks(level, portal);
|
this.syncPortalBlocks(level, portal);
|
||||||
this.syncPortalBlocks(level, other);
|
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 {
|
if (stack.getItem() == this) {
|
||||||
Antarchy.LOGGER.info("Portal gun portal waiting for counterpart owner={} side={} portal={}", player.getUUID(), side, portal.getUUID());
|
this.setLastSide(stack, side);
|
||||||
}
|
}
|
||||||
String openPath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_open_blue" : "portal_open_orange";
|
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) {
|
public ItemStack findMatchingGunStack(Player player, UUID gunId) {
|
||||||
@@ -199,21 +257,22 @@ public class PortalGunItem extends Item implements GeoItem {
|
|||||||
return ItemStack.EMPTY;
|
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) {
|
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
|
||||||
return false;
|
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) {
|
if (existing != null) {
|
||||||
existing.discard();
|
existing.discard();
|
||||||
}
|
}
|
||||||
this.setMoonSide(stack, side);
|
this.setMoonSide(stack, side);
|
||||||
this.setLastSide(stack, side);
|
this.setLastSide(stack, side);
|
||||||
this.triggerFireAnimation(level, player, stack);
|
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.awardStat(Stats.ITEM_USED.get(this));
|
||||||
player.getCooldowns().addCooldown(this, 6);
|
player.getCooldowns().addCooldown(this, 6);
|
||||||
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange", 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);
|
this.spawnShotTrail(level, player, player.getEyePosition().add(player.getLookAngle().scale(64.0D)), side);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -224,7 +283,7 @@ public class PortalGunItem extends Item implements GeoItem {
|
|||||||
blackHole.configure(player.getUUID());
|
blackHole.configure(player.getUUID());
|
||||||
blackHole.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, 0.0F, 0.0F);
|
blackHole.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, 0.0F, 0.0F);
|
||||||
level.addFreshEntity(blackHole);
|
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;
|
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) {
|
private void spawnProjectile(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||||
UUID gunId = this.ensureGunId(stack);
|
UUID gunId = this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString());
|
||||||
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), player, level);
|
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), shooter, level);
|
||||||
projectile.configure(side, gunId, stack);
|
projectile.configure(side, gunId, stack);
|
||||||
Vec3 look = player.getLookAngle();
|
Vec3 look = shooter.getLookAngle();
|
||||||
Vec3 start = player.getEyePosition().add(look.scale(0.4D));
|
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.setPos(start.x, start.y, start.z);
|
||||||
projectile.setDeltaMovement(look.scale(2.5D));
|
projectile.setDeltaMovement(look.scale(projectile.getProjectileSpeed()));
|
||||||
|
projectile.syncLaunchState();
|
||||||
level.addFreshEntity(projectile);
|
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;
|
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()) {
|
if (otherId.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -262,8 +339,8 @@ public class PortalGunItem extends Item implements GeoItem {
|
|||||||
return entity instanceof PortalGunPortalEntity portal ? portal : null;
|
return entity instanceof PortalGunPortalEntity portal ? portal : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) {
|
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
|
||||||
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, side);
|
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location());
|
||||||
if (portalId.isEmpty()) {
|
if (portalId.isEmpty()) {
|
||||||
return null;
|
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));
|
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 facing = hitResult.getDirection();
|
||||||
Direction heightAxis = this.resolveUpAxis(player, facing);
|
Direction heightAxis = this.resolveUpAxis(shooter, facing);
|
||||||
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
|
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
|
||||||
float yaw = PortalGunPlacement.yawFor(facing, heightAxis);
|
float yaw = PortalGunPlacement.yawFor(facing, heightAxis);
|
||||||
BlockPos hitPos = hitResult.getBlockPos();
|
BlockPos hitPos = hitResult.getBlockPos();
|
||||||
Vec3 hitLocation = hitResult.getLocation();
|
int maxRadius = Math.max(width, height);
|
||||||
UUID replacingPortalId = replacingPortal == null ? null : replacingPortal.getUUID();
|
List<Integer> radiusOrder = new java.util.ArrayList<>(maxRadius);
|
||||||
PortalGunPlacement bestPlacement = null;
|
for (int radius = 0; radius < maxRadius; radius++) {
|
||||||
double bestScore = Double.MAX_VALUE;
|
radiusOrder.add(radius);
|
||||||
for (int widthOffset = -3; widthOffset <= 3; widthOffset++) {
|
}
|
||||||
for (int heightOffset = -4; heightOffset <= 3; heightOffset++) {
|
int preferredRadius = maxRadius / 2;
|
||||||
BlockPos supportOrigin = hitPos.relative(widthAxis, widthOffset).relative(heightAxis, heightOffset);
|
radiusOrder.remove(Integer.valueOf(preferredRadius));
|
||||||
if (!this.canPlaceAt(level, player.getUUID(), side, supportOrigin, facing, heightAxis, replacingPortalId)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
BlockPos masterPos = supportOrigin.relative(facing);
|
BlockPos[] portalSpots = new BlockPos[supports.size()];
|
||||||
BlockPos basePos = masterPos.relative(heightAxis);
|
boolean valid = true;
|
||||||
BlockPos[] portalSpots = new BlockPos[] {masterPos, basePos};
|
int spotIndex = 0;
|
||||||
Vec3 firstCellCenter = masterPos.getCenter();
|
for (BlockPos supportPos : supports) {
|
||||||
Vec3 center = firstCellCenter
|
BlockPos portalPos = supportPos.relative(facing);
|
||||||
.add(Vec3.atLowerCornerOf(heightAxis.getNormal()).scale(0.5D))
|
BlockState supportState = level.getBlockState(supportPos);
|
||||||
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
|
BlockState portalState = level.getBlockState(portalPos);
|
||||||
Vec3 local = hitLocation.subtract(center);
|
if (!supportState.isFaceSturdy(level, supportPos, facing)
|
||||||
double widthLocal = local.dot(Vec3.atLowerCornerOf(widthAxis.getNormal()));
|
|| !this.isPortalSpaceAvailable(level, portalPos, portalState, ownerId, channelName, side, facing)) {
|
||||||
double heightLocal = local.dot(Vec3.atLowerCornerOf(heightAxis.getNormal()));
|
valid = false;
|
||||||
double depthLocal = local.dot(Vec3.atLowerCornerOf(facing.getNormal()));
|
break;
|
||||||
double widthDistance = Math.max(0.0D, Math.abs(widthLocal) - 0.5D);
|
}
|
||||||
double heightDistance = Math.max(0.0D, Math.abs(heightLocal) - 1.0D);
|
portalSpots[spotIndex++] = portalPos;
|
||||||
double score = widthDistance * widthDistance
|
}
|
||||||
+ heightDistance * heightDistance
|
if (!valid) {
|
||||||
+ Math.abs(depthLocal) * 0.25D
|
continue;
|
||||||
+ supportOrigin.distSqr(hitPos) * 0.12D
|
}
|
||||||
+ Math.abs(widthOffset) * 0.04D
|
BlockPos masterPos = null;
|
||||||
+ Math.abs(heightOffset) * 0.025D
|
for (BlockPos portalSpot : portalSpots) {
|
||||||
+ (widthOffset != 0 || heightOffset != 0 ? 0.02D : 0.0D);
|
BlockState state = level.getBlockState(portalSpot);
|
||||||
if (score < bestScore) {
|
boolean belongsToReplacingPortal = replacingPortal != null && replacingPortal.containsPortalSpot(portalSpot);
|
||||||
bestScore = score;
|
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<>();
|
Set<BlockPos> compensatedSpots = new HashSet<>();
|
||||||
if (widthOffset != 0 || heightOffset != 0) {
|
for (BlockPos portalSpot : portalSpots) {
|
||||||
compensatedSpots.add(hitPos.relative(facing));
|
center = center.add(portalSpot.getCenter());
|
||||||
compensatedSpots.add(masterPos);
|
if (horizontalOffset != 0 || verticalOffset != 0) {
|
||||||
compensatedSpots.add(basePos);
|
compensatedSpots.add(portalSpot);
|
||||||
compensatedSpots.add(supportOrigin);
|
compensatedSpots.add(portalSpot.relative(facing.getOpposite()));
|
||||||
compensatedSpots.add(basePos.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) {
|
if (facing.getAxis() != Direction.Axis.Y) {
|
||||||
return Direction.UP;
|
return Direction.UP;
|
||||||
}
|
}
|
||||||
Direction horizontal = player.getDirection();
|
Direction horizontal = shooter.getDirection();
|
||||||
if (horizontal.getAxis() == Direction.Axis.Y) {
|
if (horizontal.getAxis() == Direction.Axis.Y) {
|
||||||
return Direction.NORTH;
|
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) {
|
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, Direction face) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||||
if (blockEntity instanceof PortalGunPortalMasterBlockEntity master) {
|
if (blockEntity instanceof PortalGunPortalCellAccess portalCell) {
|
||||||
return ownerId != null
|
PortalGunPortalFaceRecord record = portalCell.portalGun$getFaceRecord(face);
|
||||||
&& ownerId.equals(master.getOwnerId())
|
if (record == null) {
|
||||||
&& master.getSide() == side
|
if (blockEntity instanceof PortalGunPortalMasterBlockEntity master && master.getPortalId() != null && master.getFacing() == face) {
|
||||||
&& (replacingPortalId == null || replacingPortalId.equals(master.getPortalId()));
|
return ownerId != null && ownerId.equals(master.getOwnerId()) && channelName.equals(master.getChannelName()) && master.getSide() == side;
|
||||||
}
|
|
||||||
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base) {
|
|
||||||
return ownerId != null
|
|
||||||
&& ownerId.equals(base.getOwnerId())
|
|
||||||
&& base.getSide() == side
|
|
||||||
&& (replacingPortalId == null || replacingPortalId.equals(base.getPortalId()));
|
|
||||||
}
|
}
|
||||||
|
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base && base.getPortalId() != null) {
|
||||||
|
BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos());
|
||||||
|
if (!(masterEntity instanceof PortalGunPortalMasterBlockEntity master)) {
|
||||||
return false;
|
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) {
|
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) {
|
||||||
if (!level.setBlock(placement.masterPos(), this.portalMasterBlock.get().defaultBlockState(), 3)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (!level.setBlock(placement.basePos(), this.portalBaseBlock.get().defaultBlockState(), 3)) {
|
existing = level.getBlockEntity(spot);
|
||||||
level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3);
|
}
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(level.getBlockEntity(placement.masterPos()) instanceof PortalGunPortalMasterBlockEntity master) || !(level.getBlockEntity(placement.basePos()) instanceof PortalGunPortalBaseBlockEntity base)) {
|
if (masterRole && existing instanceof PortalGunPortalMasterBlockEntity master
|
||||||
level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3);
|
&& (master.getPortalId() == null || master.getPortalId().equals(portal.getUUID()))) {
|
||||||
level.setBlock(placement.basePos(), Blocks.AIR.defaultBlockState(), 3);
|
master.configure(ownerId, gunId, ownerIdentity, channelName, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), pairTime, placement.width(), placement.height());
|
||||||
return false;
|
} else if (!masterRole && existing instanceof PortalGunPortalBaseBlockEntity base
|
||||||
}
|
&& (base.getPortalId() == null || base.getPortalId().equals(portal.getUUID()))) {
|
||||||
master.configure(ownerId, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), level.getServer().getTickCount());
|
|
||||||
base.configure(ownerId, portal.getUUID(), side, placement.masterPos());
|
base.configure(ownerId, portal.getUUID(), side, placement.masterPos());
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncPortalBlocks(ServerLevel level, PortalGunPortalEntity portal) {
|
private void syncPortalBlocks(ServerLevel level, PortalGunPortalEntity portal) {
|
||||||
if (level.getBlockEntity(portal.getMasterPos()) instanceof PortalGunPortalMasterBlockEntity master) {
|
for (BlockPos portalSpot : portal.getPortalSpots()) {
|
||||||
master.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
|
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());
|
base.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setLastSide(ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
private void setLastSide(ItemStack stack, PortalGunPortalEntity.PortalSide side) {
|
||||||
if (side == null) {
|
if (side == null) {
|
||||||
@@ -452,16 +640,115 @@ public class PortalGunItem extends Item implements GeoItem {
|
|||||||
return side.name().equals(sideName);
|
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);
|
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);
|
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;
|
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) {
|
private boolean matchesGunId(ItemStack stack, UUID gunId) {
|
||||||
if (stack.isEmpty() || stack.getItem() != this) {
|
if (stack.isEmpty() || stack.getItem() != this) {
|
||||||
return false;
|
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) {
|
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
|
||||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.primary").withStyle(ChatFormatting.GRAY));
|
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.primary").withStyle(ChatFormatting.GRAY));
|
||||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.secondary").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.reset").withStyle(ChatFormatting.DARK_GRAY));
|
||||||
|
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.size", getPortalWidth(stack), getPortalHeight(stack)).withStyle(ChatFormatting.GRAY));
|
||||||
|
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.grab_strength", getGrabStrength(stack)).withStyle(ChatFormatting.GRAY));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+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 com.craisinlord.antarchy.Antarchy;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
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 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
|
@Override
|
||||||
public Type<PortalGunPrimaryPayload> type() {
|
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 java.util.UUID;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.sounds.SoundEvents;
|
|
||||||
import net.minecraft.sounds.SoundSource;
|
import net.minecraft.sounds.SoundSource;
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
@@ -73,7 +72,7 @@ public class PortalGunBlackHoleEntity extends Entity implements GeoEntity {
|
|||||||
}
|
}
|
||||||
this.pullNearby(serverLevel);
|
this.pullNearby(serverLevel);
|
||||||
if (this.ageTicks % 10 == 0) {
|
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;
|
return collided;
|
||||||
}
|
}
|
||||||
AABB pathBox = startBox.expandTowards(movement).inflate(SEARCH_PADDING);
|
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()) {
|
if (nearby.isEmpty()) {
|
||||||
return collided;
|
return collided;
|
||||||
}
|
}
|
||||||
Vec3 adjusted = collided;
|
Vec3 adjusted = collided;
|
||||||
for (Entity candidate : nearby) {
|
for (PortalGunPortalEntity portal : nearby) {
|
||||||
PortalGunPortalEntity portal = (PortalGunPortalEntity) candidate;
|
|
||||||
Vec3 candidateAdjusted = resolveCollisionAgainstPortal(entity, startBox, movement, adjusted, portal);
|
Vec3 candidateAdjusted = resolveCollisionAgainstPortal(entity, startBox, movement, adjusted, portal);
|
||||||
if (candidateAdjusted.distanceToSqr(movement) < adjusted.distanceToSqr(movement)) {
|
if (candidateAdjusted.distanceToSqr(movement) < adjusted.distanceToSqr(movement)) {
|
||||||
adjusted = candidateAdjusted;
|
adjusted = candidateAdjusted;
|
||||||
@@ -39,6 +38,10 @@ public final class PortalGunCollisionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Vec3 resolveCollisionAgainstPortal(Entity entity, AABB startBox, Vec3 movement, Vec3 collided, PortalGunPortalEntity portal) {
|
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();
|
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
|
||||||
Vec3 normal = portal.getNormalVec().normalize();
|
Vec3 normal = portal.getNormalVec().normalize();
|
||||||
double desiredNormal = movement.dot(normal);
|
double desiredNormal = movement.dot(normal);
|
||||||
@@ -60,21 +63,21 @@ public final class PortalGunCollisionHelper {
|
|||||||
&& !portal.getPortalInsides(entity).intersects(endBox)
|
&& !portal.getPortalInsides(entity).intersects(endBox)
|
||||||
&& !intersectsPortalWindow(startBox, shape)
|
&& !intersectsPortalWindow(startBox, shape)
|
||||||
&& !intersectsPortalWindow(endBox, shape)
|
&& !intersectsPortalWindow(endBox, shape)
|
||||||
&& !segmentIntersectsPortalWindow(shape, startBox, endBox)) {
|
&& !segmentIntersectsPortalWindow(entity, portal, startBox, endBox)) {
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
Vec3 startCenter = startBox.getCenter();
|
PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startProbe);
|
||||||
Vec3 endCenter = endBox.getCenter();
|
PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endProbe);
|
||||||
PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startCenter);
|
|
||||||
PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endCenter);
|
|
||||||
return startCoords.depth() > 0.0D
|
return startCoords.depth() > 0.0D
|
||||||
&& endCoords.depth() >= -PLANE_TOLERANCE
|
&& endCoords.depth() >= -PLANE_TOLERANCE
|
||||||
&& Math.abs(endCoords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
|
&& Math.abs(endCoords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
|
||||||
&& Math.abs(endCoords.vertical()) <= shape.halfHeight() + 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) {
|
private static boolean intersectsPortalWindow(AABB box, PortalGunWorldPortalShape shape) {
|
||||||
@@ -90,39 +93,17 @@ public final class PortalGunCollisionHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean segmentIntersectsPortalWindow(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) {
|
private static boolean segmentIntersectsPortalWindow(Entity entity, PortalGunPortalEntity portal, AABB startBox, AABB endBox) {
|
||||||
for (Vec3 startCorner : corners(startBox)) {
|
return portal.crossesPortal(portal.teleportProbePosition(entity, startBox), portal.teleportProbePosition(entity, endBox));
|
||||||
Vec3 endCorner = matchSample(startCorner, startBox, endBox);
|
|
||||||
if (shape.crosses(startCorner, endCorner)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) {
|
private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, Vec3 startProbe, Vec3 endProbe) {
|
||||||
double minHorizontal = Double.POSITIVE_INFINITY;
|
PortalGunWorldPortalShape.PortalLocalCoords start = shape.localCoords(startProbe);
|
||||||
double maxHorizontal = Double.NEGATIVE_INFINITY;
|
PortalGunWorldPortalShape.PortalLocalCoords end = shape.localCoords(endProbe);
|
||||||
double minVertical = Double.POSITIVE_INFINITY;
|
return Math.abs(start.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE
|
||||||
double maxVertical = Double.NEGATIVE_INFINITY;
|
&& Math.abs(start.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE
|
||||||
for (Vec3 corner : corners(startBox)) {
|
&& Math.abs(end.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE
|
||||||
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
|
&& Math.abs(end.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE;
|
||||||
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 Vec3 applyBorderCollision(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal) {
|
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;
|
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) {
|
private static Vec3[] corners(AABB box) {
|
||||||
return new Vec3[] {
|
return new Vec3[] {
|
||||||
new Vec3(box.minX, box.minY, box.minZ),
|
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;
|
package com.craisinlord.antarchy.content.portalgun;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
@@ -15,13 +16,18 @@ public record PortalGunPlacement(
|
|||||||
BlockPos masterPos,
|
BlockPos masterPos,
|
||||||
BlockPos basePos,
|
BlockPos basePos,
|
||||||
BlockPos[] portalSpots,
|
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);
|
Direction widthAxis = widthAxis(facing, upAxis);
|
||||||
BlockPos supportOrigin = masterPos.relative(facing.getOpposite());
|
BlockPos supportOrigin = masterPos.relative(facing.getOpposite());
|
||||||
Vec3 center = masterPos.getCenter()
|
Vec3 center = Vec3.ZERO;
|
||||||
.add(Vec3.atLowerCornerOf(upAxis.getNormal()).scale(0.5D))
|
for (BlockPos portalSpot : portalSpots) {
|
||||||
|
center = center.add(portalSpot.getCenter());
|
||||||
|
}
|
||||||
|
center = center.scale(1.0D / portalSpots.length)
|
||||||
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
|
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
|
||||||
return new PortalGunPlacement(
|
return new PortalGunPlacement(
|
||||||
center,
|
center,
|
||||||
@@ -32,8 +38,10 @@ public record PortalGunPlacement(
|
|||||||
supportOrigin,
|
supportOrigin,
|
||||||
masterPos,
|
masterPos,
|
||||||
basePos,
|
basePos,
|
||||||
new BlockPos[] {masterPos, basePos},
|
portalSpots,
|
||||||
compensatedSpots
|
compensatedSpots,
|
||||||
|
width,
|
||||||
|
height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,4 +55,47 @@ public record PortalGunPlacement(
|
|||||||
public static float yawFor(Direction facing, Direction upAxis) {
|
public static float yawFor(Direction facing, Direction upAxis) {
|
||||||
return facing.getAxis() == Direction.Axis.Y ? upAxis.toYRot() : facing.toYRot();
|
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;
|
package com.craisinlord.antarchy.content.portalgun;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.content.item.PortalGunItem;
|
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 java.util.UUID;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||||
import net.minecraft.network.syncher.SynchedEntityData;
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
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.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.EntityDimensions;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Pose;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.entity.projectile.ThrowableItemProjectile;
|
import net.minecraft.world.entity.projectile.ThrowableItemProjectile;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
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.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.BlockHitResult;
|
||||||
import net.minecraft.world.phys.EntityHitResult;
|
import net.minecraft.world.phys.EntityHitResult;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
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> 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> 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> DISTANCE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
||||||
private static final EntityDataAccessor<Integer> SPAWN_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
private static final EntityDataAccessor<Long> SPAWN_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG);
|
||||||
private static final EntityDataAccessor<Integer> SPAWN_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
private static final EntityDataAccessor<Long> SPAWN_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG);
|
||||||
private static final EntityDataAccessor<Integer> SPAWN_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
|
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_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_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT);
|
||||||
private static final EntityDataAccessor<Float> VELOCITY_Z = 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 UUID gunId;
|
||||||
private boolean spawnSynced;
|
private boolean spawnSynced;
|
||||||
|
private boolean ticketRegistered;
|
||||||
public int portalWidth = 1;
|
public int portalWidth = 1;
|
||||||
public int portalHeight = 2;
|
public int portalHeight = 2;
|
||||||
|
|
||||||
@@ -53,30 +85,55 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
|||||||
this.noCulling = true;
|
this.noCulling = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityDimensions getDimensions(Pose pose) {
|
||||||
|
return EntityDimensions.fixed(0.3F, 0.3F);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||||
super.defineSynchedData(builder);
|
super.defineSynchedData(builder);
|
||||||
builder.define(SIDE, PortalGunPortalEntity.PortalSide.BLUE.ordinal());
|
builder.define(SIDE, PortalGunPortalEntity.PortalSide.BLUE.ordinal());
|
||||||
builder.define(SHOOTER_ID, -1);
|
builder.define(SHOOTER_ID, -1);
|
||||||
builder.define(DISTANCE, 0);
|
builder.define(DISTANCE, 0);
|
||||||
builder.define(SPAWN_X, 0);
|
builder.define(SPAWN_X, Double.doubleToRawLongBits(0.0D));
|
||||||
builder.define(SPAWN_Y, 0);
|
builder.define(SPAWN_Y, Double.doubleToRawLongBits(0.0D));
|
||||||
builder.define(SPAWN_Z, 0);
|
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_X, 0.0F);
|
||||||
builder.define(VELOCITY_Y, 0.0F);
|
builder.define(VELOCITY_Y, 0.0F);
|
||||||
builder.define(VELOCITY_Z, 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) {
|
public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) {
|
||||||
this.entityData.set(SIDE, side.ordinal());
|
this.entityData.set(SIDE, side.ordinal());
|
||||||
this.gunId = gunId;
|
this.gunId = gunId;
|
||||||
this.setItem(gunStack.copyWithCount(1));
|
this.setItem(gunStack.copyWithCount(1));
|
||||||
|
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();
|
Entity owner = this.getOwner();
|
||||||
this.entityData.set(SHOOTER_ID, owner == null ? -1 : owner.getId());
|
this.entityData.set(SHOOTER_ID, owner == null ? -1 : owner.getId());
|
||||||
this.syncSpawnPosition(this.blockPosition());
|
}
|
||||||
|
|
||||||
|
public void syncLaunchState() {
|
||||||
|
this.syncSpawnPosition(this.position());
|
||||||
this.syncVelocity();
|
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() {
|
public PortalGunPortalEntity.PortalSide getPortalSide() {
|
||||||
return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
}
|
}
|
||||||
@@ -89,8 +146,16 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
|||||||
return this.entityData.get(DISTANCE);
|
return this.entityData.get(DISTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockPos getSyncedSpawnPos() {
|
public int getMaxTravelDistance() {
|
||||||
return new BlockPos(this.entityData.get(SPAWN_X), this.entityData.get(SPAWN_Y), this.entityData.get(SPAWN_Z));
|
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() {
|
public Vec3 getSyncedVelocity() {
|
||||||
@@ -124,22 +189,82 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
if (this.level().isClientSide && !this.spawnSynced) {
|
||||||
if (!this.spawnSynced) {
|
Vec3 spawnPosition = this.getSyncedSpawnPosition();
|
||||||
this.syncSpawnPosition(this.blockPosition());
|
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.syncVelocity();
|
||||||
|
this.spawnSynced = true;
|
||||||
|
}
|
||||||
this.entityData.set(DISTANCE, this.tickCount);
|
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();
|
this.discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Vec3 motion = this.getDeltaMovement();
|
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) {
|
if (motion.lengthSqr() > 1.0E-6D) {
|
||||||
double horizontal = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
|
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.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.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) {
|
if (this.level().isClientSide) {
|
||||||
Vec3 point = this.position();
|
Vec3 point = this.position();
|
||||||
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
|
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
|
@Override
|
||||||
protected void onHitEntity(EntityHitResult result) {
|
protected void onHitEntity(EntityHitResult result) {
|
||||||
}
|
}
|
||||||
@@ -161,13 +343,30 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
|||||||
if (!this.level().isClientSide && this.level() instanceof ServerLevel serverLevel) {
|
if (!this.level().isClientSide && this.level() instanceof ServerLevel serverLevel) {
|
||||||
this.spawnImpactParticles(serverLevel, result.getLocation());
|
this.spawnImpactParticles(serverLevel, result.getLocation());
|
||||||
ItemStack sourceStack = this.resolveSourceStack();
|
ItemStack sourceStack = this.resolveSourceStack();
|
||||||
if (this.getOwner() instanceof Player player && sourceStack.getItem() instanceof PortalGunItem portalGunItem) {
|
if (this.getOwner() instanceof LivingEntity shooter && sourceStack.getItem() instanceof PortalGunItem portalGunItem) {
|
||||||
portalGunItem.handlePortalImpact(serverLevel, player, sourceStack, this.getPortalSide(), result, this.position());
|
portalGunItem.handlePortalImpact(serverLevel, shooter, sourceStack, this.gunId, this.getPortalSide(), result, result.getLocation(), this.portalWidth, this.portalHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.discard();
|
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) {
|
private void spawnImpactParticles(ServerLevel level, Vec3 impactPos) {
|
||||||
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
|
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
|
||||||
level.sendParticles(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D);
|
level.sendParticles(net.minecraft.core.particles.ParticleTypes.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);
|
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) {
|
private void syncSpawnPosition(Vec3 pos) {
|
||||||
this.entityData.set(SPAWN_X, pos.getX());
|
this.entityData.set(SPAWN_X, Double.doubleToRawLongBits(pos.x));
|
||||||
this.entityData.set(SPAWN_Y, pos.getY());
|
this.entityData.set(SPAWN_Y, Double.doubleToRawLongBits(pos.y));
|
||||||
this.entityData.set(SPAWN_Z, pos.getZ());
|
this.entityData.set(SPAWN_Z, Double.doubleToRawLongBits(pos.z));
|
||||||
this.spawnSynced = true;
|
this.spawnSynced = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,12 +406,20 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
|||||||
tag.putInt("Side", this.entityData.get(SIDE));
|
tag.putInt("Side", this.entityData.get(SIDE));
|
||||||
tag.putInt("ShooterId", this.entityData.get(SHOOTER_ID));
|
tag.putInt("ShooterId", this.entityData.get(SHOOTER_ID));
|
||||||
tag.putInt("Distance", this.entityData.get(DISTANCE));
|
tag.putInt("Distance", this.entityData.get(DISTANCE));
|
||||||
tag.putInt("SpawnX", this.entityData.get(SPAWN_X));
|
Vec3 spawnPosition = this.getSyncedSpawnPosition();
|
||||||
tag.putInt("SpawnY", this.entityData.get(SPAWN_Y));
|
tag.putInt("SpawnX", net.minecraft.util.Mth.floor(spawnPosition.x));
|
||||||
tag.putInt("SpawnZ", this.entityData.get(SPAWN_Z));
|
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("VelocityX", this.entityData.get(VELOCITY_X));
|
||||||
tag.putFloat("VelocityY", this.entityData.get(VELOCITY_Y));
|
tag.putFloat("VelocityY", this.entityData.get(VELOCITY_Y));
|
||||||
tag.putFloat("VelocityZ", this.entityData.get(VELOCITY_Z));
|
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("PortalWidth", this.portalWidth);
|
||||||
tag.putInt("PortalHeight", this.portalHeight);
|
tag.putInt("PortalHeight", this.portalHeight);
|
||||||
if (this.gunId != null) {
|
if (this.gunId != null) {
|
||||||
@@ -226,14 +433,20 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
|
|||||||
this.entityData.set(SIDE, tag.getInt("Side"));
|
this.entityData.set(SIDE, tag.getInt("Side"));
|
||||||
this.entityData.set(SHOOTER_ID, tag.getInt("ShooterId"));
|
this.entityData.set(SHOOTER_ID, tag.getInt("ShooterId"));
|
||||||
this.entityData.set(DISTANCE, tag.getInt("Distance"));
|
this.entityData.set(DISTANCE, tag.getInt("Distance"));
|
||||||
this.entityData.set(SPAWN_X, tag.getInt("SpawnX"));
|
this.entityData.set(SPAWN_X, Double.doubleToRawLongBits(tag.contains("SpawnPosX") ? tag.getDouble("SpawnPosX") : tag.getInt("SpawnX")));
|
||||||
this.entityData.set(SPAWN_Y, tag.getInt("SpawnY"));
|
this.entityData.set(SPAWN_Y, Double.doubleToRawLongBits(tag.contains("SpawnPosY") ? tag.getDouble("SpawnPosY") : tag.getInt("SpawnY")));
|
||||||
this.entityData.set(SPAWN_Z, tag.getInt("SpawnZ"));
|
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_X, tag.getFloat("VelocityX"));
|
||||||
this.entityData.set(VELOCITY_Y, tag.getFloat("VelocityY"));
|
this.entityData.set(VELOCITY_Y, tag.getFloat("VelocityY"));
|
||||||
this.entityData.set(VELOCITY_Z, tag.getFloat("VelocityZ"));
|
this.entityData.set(VELOCITY_Z, tag.getFloat("VelocityZ"));
|
||||||
this.portalWidth = Math.max(1, tag.getInt("PortalWidth"));
|
this.entityData.set(PROJECTILE_SPEED, tag.contains("ProjectileSpeed") ? tag.getFloat("ProjectileSpeed") : 4.99F);
|
||||||
this.portalHeight = Math.max(2, tag.getInt("PortalHeight"));
|
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;
|
this.spawnSynced = true;
|
||||||
if (tag.hasUUID("GunId")) {
|
if (tag.hasUUID("GunId")) {
|
||||||
this.gunId = tag.getUUID("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;
|
package com.craisinlord.antarchy.content.portalgun;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.entity.Entity;
|
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 ID = "antarchy_portal_gun_pairs";
|
||||||
private static final String ENTRIES_KEY = "Entries";
|
private static final String ENTRIES_KEY = "Entries";
|
||||||
private static final String OWNER_KEY = "Owner";
|
private static final String 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 BLUE_KEY = "Blue";
|
||||||
private static final String ORANGE_KEY = "Orange";
|
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() {
|
public static PortalGunSavedData create() {
|
||||||
return new PortalGunSavedData();
|
return new PortalGunSavedData();
|
||||||
@@ -31,7 +41,20 @@ public final class PortalGunSavedData extends SavedData {
|
|||||||
UUID owner = entryTag.getUUID(OWNER_KEY);
|
UUID owner = entryTag.getUUID(OWNER_KEY);
|
||||||
UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null;
|
UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null;
|
||||||
UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
|
UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
|
||||||
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;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -39,21 +62,44 @@ public final class PortalGunSavedData extends SavedData {
|
|||||||
@Override
|
@Override
|
||||||
public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) {
|
public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
ListTag entries = new ListTag();
|
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();
|
CompoundTag entryTag = new CompoundTag();
|
||||||
entryTag.putUUID(OWNER_KEY, entry.getKey());
|
entryTag.putUUID(OWNER_KEY, entry.getKey().owner());
|
||||||
if (entry.getValue().bluePortalId() != null) {
|
entryTag.putString(DIMENSION_KEY, entry.getKey().dimension().toString());
|
||||||
entryTag.putUUID(BLUE_KEY, entry.getValue().bluePortalId());
|
if (entry.getKey().gunId() != null) {
|
||||||
}
|
entryTag.putUUID(GUN_KEY, entry.getKey().gunId());
|
||||||
if (entry.getValue().orangePortalId() != null) {
|
|
||||||
entryTag.putUUID(ORANGE_KEY, entry.getValue().orangePortalId());
|
|
||||||
}
|
}
|
||||||
|
writePair(entryTag, entry.getValue());
|
||||||
entries.add(entryTag);
|
entries.add(entryTag);
|
||||||
}
|
}
|
||||||
|
for (Map.Entry<UUID, PairRecord> entry : this.legacyPairs.entrySet()) {
|
||||||
|
CompoundTag entryTag = new CompoundTag();
|
||||||
|
entryTag.putUUID(OWNER_KEY, entry.getKey());
|
||||||
|
writePair(entryTag, entry.getValue());
|
||||||
|
entries.add(entryTag);
|
||||||
|
}
|
||||||
|
ListTag channels = new ListTag();
|
||||||
|
for (Map.Entry<OwnerChannelKey, UUID> entry : this.channelGunIds.entrySet()) {
|
||||||
|
CompoundTag channelTag = new CompoundTag();
|
||||||
|
channelTag.putUUID(OWNER_KEY, entry.getKey().owner());
|
||||||
|
channelTag.putString("Channel", entry.getKey().channelName());
|
||||||
|
channelTag.putUUID(GUN_KEY, entry.getValue());
|
||||||
|
channels.add(channelTag);
|
||||||
|
}
|
||||||
tag.put(ENTRIES_KEY, entries);
|
tag.put(ENTRIES_KEY, entries);
|
||||||
|
tag.put(CHANNELS_KEY, channels);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void writePair(CompoundTag tag, PairRecord record) {
|
||||||
|
if (record.bluePortalId() != null) {
|
||||||
|
tag.putUUID(BLUE_KEY, record.bluePortalId());
|
||||||
|
}
|
||||||
|
if (record.orangePortalId() != null) {
|
||||||
|
tag.putUUID(ORANGE_KEY, record.orangePortalId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static PortalGunSavedData get(MinecraftServer server) {
|
private static PortalGunSavedData get(MinecraftServer server) {
|
||||||
return server.overworld().getDataStorage().computeIfAbsent(
|
return server.overworld().getDataStorage().computeIfAbsent(
|
||||||
new SavedData.Factory<>(PortalGunSavedData::create, PortalGunSavedData::load, null),
|
new SavedData.Factory<>(PortalGunSavedData::create, PortalGunSavedData::load, null),
|
||||||
@@ -61,35 +107,95 @@ public final class PortalGunSavedData extends SavedData {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side) {
|
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
|
||||||
PairRecord record = get(server).pairs.get(owner);
|
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) {
|
if (record == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
data.synchronizeLoadedPortal(server, owner, dimension, record.bluePortalId(), resolvedGunId, channelName);
|
||||||
|
data.synchronizeLoadedPortal(server, owner, dimension, record.orangePortalId(), resolvedGunId, channelName);
|
||||||
return Optional.ofNullable(side == PortalGunPortalEntity.PortalSide.BLUE ? record.bluePortalId() : record.orangePortalId());
|
return Optional.ofNullable(side == PortalGunPortalEntity.PortalSide.BLUE ? record.bluePortalId() : record.orangePortalId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isRegistered(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID portalId) {
|
public static Optional<UUID> getPortalId(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
|
||||||
return getPortalId(server, owner, side).map(portalId::equals).orElse(false);
|
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);
|
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
|
PairRecord updated = side == PortalGunPortalEntity.PortalSide.BLUE
|
||||||
? new PairRecord(portalId, current.orangePortalId())
|
? new PairRecord(portalId, current.orangePortalId())
|
||||||
: new PairRecord(current.bluePortalId(), portalId);
|
: new PairRecord(current.bluePortalId(), portalId);
|
||||||
if (updated.bluePortalId() == null && updated.orangePortalId() == null) {
|
if (updated.bluePortalId() == null && updated.orangePortalId() == null) {
|
||||||
data.pairs.remove(owner);
|
data.pairs.remove(key);
|
||||||
} else {
|
} else {
|
||||||
data.pairs.put(owner, updated);
|
data.pairs.put(key, updated);
|
||||||
}
|
}
|
||||||
data.setDirty();
|
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);
|
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) {
|
if (current == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -102,48 +208,247 @@ public final class PortalGunSavedData extends SavedData {
|
|||||||
orange = null;
|
orange = null;
|
||||||
}
|
}
|
||||||
if (blue == null && orange == null) {
|
if (blue == null && orange == null) {
|
||||||
data.pairs.remove(owner);
|
data.pairs.remove(key);
|
||||||
} else {
|
} else {
|
||||||
data.pairs.put(owner, new PairRecord(blue, orange));
|
data.pairs.put(key, new PairRecord(blue, orange));
|
||||||
}
|
}
|
||||||
data.setDirty();
|
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);
|
PortalGunSavedData data = get(server);
|
||||||
PairRecord current = data.pairs.remove(owner);
|
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
|
||||||
data.setDirty();
|
data.migrateLegacyPair(server, owner);
|
||||||
if (current == null) {
|
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
|
||||||
return;
|
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) {
|
public static void clearPortalSide(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) {
|
||||||
Optional<UUID> portalId = getPortalId(server, owner, 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()) {
|
if (portalId.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (ServerLevel level : server.getAllLevels()) {
|
|
||||||
Entity entity = level.getEntity(portalId.get());
|
Entity entity = level.getEntity(portalId.get());
|
||||||
if (entity instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
|
||||||
return portal;
|
}
|
||||||
|
|
||||||
|
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;
|
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) {
|
private static void discardIfLoaded(MinecraftServer server, UUID portalId) {
|
||||||
if (portalId == null) {
|
ServerLevel level = findPortalLevel(server, portalId);
|
||||||
return;
|
if (level != null && level.getEntity(portalId) instanceof PortalGunPortalEntity portal) {
|
||||||
}
|
|
||||||
for (ServerLevel level : server.getAllLevels()) {
|
|
||||||
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal) {
|
|
||||||
portal.discard();
|
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) {
|
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) {
|
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) {
|
public static Quaternionf createTransform(Vec3 sourceWidth, Vec3 sourceUp, Vec3 sourceForward, Vec3 destinationWidth, Vec3 destinationUp, Vec3 destinationForward) {
|
||||||
Matrix3f sourceBasis = new Matrix3f(
|
Matrix3f sourceBasis = new Matrix3f(
|
||||||
(float) sourceWidth.x, (float) sourceUp.x, (float) sourceForward.x,
|
(float) sourceWidth.x, (float) sourceWidth.y, (float) sourceWidth.z,
|
||||||
(float) sourceWidth.y, (float) sourceUp.y, (float) sourceForward.y,
|
(float) sourceUp.x, (float) sourceUp.y, (float) sourceUp.z,
|
||||||
(float) sourceWidth.z, (float) sourceUp.z, (float) sourceForward.z
|
(float) sourceForward.x, (float) sourceForward.y, (float) sourceForward.z
|
||||||
);
|
);
|
||||||
Matrix3f destinationBasis = new Matrix3f(
|
Matrix3f destinationBasis = new Matrix3f(
|
||||||
(float) destinationWidth.x, (float) destinationUp.x, (float) destinationForward.x,
|
(float) destinationWidth.x, (float) destinationWidth.y, (float) destinationWidth.z,
|
||||||
(float) destinationWidth.y, (float) destinationUp.y, (float) destinationForward.y,
|
(float) destinationUp.x, (float) destinationUp.y, (float) destinationUp.z,
|
||||||
(float) destinationWidth.z, (float) destinationUp.z, (float) destinationForward.z
|
(float) destinationForward.x, (float) destinationForward.y, (float) destinationForward.z
|
||||||
);
|
);
|
||||||
Matrix3f transform = destinationBasis.mul(sourceBasis.transpose(new Matrix3f()), new Matrix3f());
|
Matrix3f transform = destinationBasis.mul(sourceBasis.transpose(new Matrix3f()), new Matrix3f());
|
||||||
return transform.getNormalizedRotation(new Quaternionf());
|
return transform.getNormalizedRotation(new Quaternionf());
|
||||||
@@ -48,9 +48,9 @@ public final class PortalGunTransformUtil {
|
|||||||
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
|
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
|
||||||
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
|
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
|
||||||
Vec3 destinationNormal = destinationPortal.getNormalVec().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(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) {
|
public static Vec3 transformVector(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 vector) {
|
||||||
@@ -60,7 +60,7 @@ public final class PortalGunTransformUtil {
|
|||||||
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
|
Vec3 destinationWidth = destinationPortal.getWidthVec().normalize();
|
||||||
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
|
Vec3 destinationUp = destinationPortal.getUpVec().normalize();
|
||||||
Vec3 destinationOut = destinationPortal.getNormalVec().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(destinationUp.scale(vector.dot(sourceUp)))
|
||||||
.add(destinationOut.scale(vector.dot(sourceInto)));
|
.add(destinationOut.scale(vector.dot(sourceInto)));
|
||||||
}
|
}
|
||||||
@@ -102,11 +102,11 @@ public final class PortalGunTransformUtil {
|
|||||||
public static Quaternionf orientationQuaternion(Vec3 look, Vec3 up) {
|
public static Quaternionf orientationQuaternion(Vec3 look, Vec3 up) {
|
||||||
Vec3 forward = look.normalize();
|
Vec3 forward = look.normalize();
|
||||||
Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).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(
|
Matrix3f basis = new Matrix3f(
|
||||||
(float) right.x, (float) correctedUp.x, (float) (-forward.x),
|
(float) right.x, (float) right.y, (float) right.z,
|
||||||
(float) right.y, (float) correctedUp.y, (float) (-forward.y),
|
(float) correctedUp.x, (float) correctedUp.y, (float) correctedUp.z,
|
||||||
(float) right.z, (float) correctedUp.z, (float) (-forward.z)
|
(float) -forward.x, (float) -forward.y, (float) -forward.z
|
||||||
);
|
);
|
||||||
return basis.getNormalizedRotation(new Quaternionf());
|
return basis.getNormalizedRotation(new Quaternionf());
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-9
@@ -35,6 +35,11 @@ public record PortalGunWorldPortalShape(
|
|||||||
return this.createPlaneAround(offset, this.halfDepth);
|
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) {
|
public Vec3[] getCorners(double normalOffset) {
|
||||||
Vec3 center = this.center.add(this.normal.scale(normalOffset));
|
Vec3 center = this.center.add(this.normal.scale(normalOffset));
|
||||||
Vec3 width = this.right.scale(this.halfWidth);
|
Vec3 width = this.right.scale(this.halfWidth);
|
||||||
@@ -76,13 +81,7 @@ public record PortalGunWorldPortalShape(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldRenderFront(Vec3 cameraPos) {
|
public boolean shouldRenderFront(Vec3 cameraPos) {
|
||||||
PortalLocalCoords coords = this.localCoords(cameraPos);
|
return this.localCoords(cameraPos).depth() > 0.0D;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean intersectsFront(AABB box, double tolerance) {
|
public boolean intersectsFront(AABB box, double tolerance) {
|
||||||
@@ -128,8 +127,8 @@ public record PortalGunWorldPortalShape(
|
|||||||
double horizontalOuter = this.halfWidth + borderThickness;
|
double horizontalOuter = this.halfWidth + borderThickness;
|
||||||
double verticalOuter = this.halfHeight + borderThickness;
|
double verticalOuter = this.halfHeight + borderThickness;
|
||||||
return List.of(
|
return List.of(
|
||||||
this.createLocalBox(-horizontalOuter, this.halfWidth, -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, 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, -verticalOuter, -this.halfHeight, -this.halfDepth, this.halfDepth),
|
||||||
this.createLocalBox(-this.halfWidth, this.halfWidth, this.halfHeight, verticalOuter, -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())) {
|
|| this.anchorId != null && this.anchorId.equals(entity.getUUID())) {
|
||||||
return false;
|
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)) {
|
if (this.chronosphere && !(entity instanceof LivingEntity || entity instanceof Projectile || entity instanceof ItemEntity)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -146,8 +146,7 @@ public final class TimeDilationManager {
|
|||||||
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
|
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)
|
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 (RoyalEffectHooks.dilatedHolder() != null && entity instanceof net.minecraft.world.entity.LivingEntity living) {
|
if (RoyalEffectHooks.dilatedHolder() != null && entity instanceof net.minecraft.world.entity.LivingEntity living) {
|
||||||
living.removeEffect(RoyalEffectHooks.dilatedHolder());
|
living.removeEffect(RoyalEffectHooks.dilatedHolder());
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-2
@@ -2,9 +2,11 @@ package com.craisinlord.antarchy.content.worldgen.thoraxis;
|
|||||||
|
|
||||||
import com.craisinlord.antarchy.Antarchy;
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Holder;
|
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("dream_dunes_max_y", 160).forGetter(ThoraxisBiomeSource::dreamDunesMaxY),
|
||||||
Codec.INT.optionalFieldOf("umbral_hills_max_y", 160).forGetter(ThoraxisBiomeSource::umbralHillsMaxY),
|
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("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));
|
).apply(instance, ThoraxisBiomeSource::new));
|
||||||
|
|
||||||
private final Climate.ParameterList<Holder<Biome>> parameters;
|
private final Climate.ParameterList<Holder<Biome>> parameters;
|
||||||
@@ -65,6 +68,7 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
|||||||
private final int umbralHillsMaxY;
|
private final int umbralHillsMaxY;
|
||||||
private final int lucidPoolsMinY;
|
private final int lucidPoolsMinY;
|
||||||
private final int nadirForestMinY;
|
private final int nadirForestMinY;
|
||||||
|
private final List<RegionalBiomeOverlay> regionalOverlays;
|
||||||
private final int dreamDunesMaxQuartY;
|
private final int dreamDunesMaxQuartY;
|
||||||
private final int umbralHillsMaxQuartY;
|
private final int umbralHillsMaxQuartY;
|
||||||
private final int lucidPoolsMinQuartY;
|
private final int lucidPoolsMinQuartY;
|
||||||
@@ -76,19 +80,21 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
|||||||
private Holder<Biome> umbralHillsBiome;
|
private Holder<Biome> umbralHillsBiome;
|
||||||
private Holder<Biome> lucidPoolsBiome;
|
private Holder<Biome> lucidPoolsBiome;
|
||||||
private Holder<Biome> nadirForestBiome;
|
private Holder<Biome> nadirForestBiome;
|
||||||
|
private List<ResolvedRegionalBiomeOverlay> resolvedRegionalOverlays = List.of();
|
||||||
|
|
||||||
private static final ThreadLocal<Long2DoubleOpenHashMap> REGION_CACHE =
|
private static final ThreadLocal<Long2DoubleOpenHashMap> REGION_CACHE =
|
||||||
ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(512));
|
ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(512));
|
||||||
private static final ThreadLocal<Long2DoubleOpenHashMap> VERTICAL_CACHE =
|
private static final ThreadLocal<Long2DoubleOpenHashMap> VERTICAL_CACHE =
|
||||||
ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(64));
|
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.parameters = parameters;
|
||||||
this.delegate = MultiNoiseBiomeSource.createFromList(parameters);
|
this.delegate = MultiNoiseBiomeSource.createFromList(parameters);
|
||||||
this.dreamDunesMaxY = dreamDunesMaxY;
|
this.dreamDunesMaxY = dreamDunesMaxY;
|
||||||
this.umbralHillsMaxY = umbralHillsMaxY;
|
this.umbralHillsMaxY = umbralHillsMaxY;
|
||||||
this.lucidPoolsMinY = lucidPoolsMinY;
|
this.lucidPoolsMinY = lucidPoolsMinY;
|
||||||
this.nadirForestMinY = nadirForestMinY;
|
this.nadirForestMinY = nadirForestMinY;
|
||||||
|
this.regionalOverlays = regionalOverlays;
|
||||||
this.dreamDunesMaxQuartY = QuartPos.fromBlock(dreamDunesMaxY);
|
this.dreamDunesMaxQuartY = QuartPos.fromBlock(dreamDunesMaxY);
|
||||||
this.umbralHillsMaxQuartY = QuartPos.fromBlock(umbralHillsMaxY);
|
this.umbralHillsMaxQuartY = QuartPos.fromBlock(umbralHillsMaxY);
|
||||||
this.lucidPoolsMinQuartY = QuartPos.fromBlock(lucidPoolsMinY);
|
this.lucidPoolsMinQuartY = QuartPos.fromBlock(lucidPoolsMinY);
|
||||||
@@ -109,6 +115,7 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
|||||||
this.umbralHillsBiome = this.findBiome(UMBRAL_HILLS);
|
this.umbralHillsBiome = this.findBiome(UMBRAL_HILLS);
|
||||||
this.lucidPoolsBiome = this.findBiome(LUCID_POOLS);
|
this.lucidPoolsBiome = this.findBiome(LUCID_POOLS);
|
||||||
this.nadirForestBiome = this.findBiome(NADIR_FOREST);
|
this.nadirForestBiome = this.findBiome(NADIR_FOREST);
|
||||||
|
this.resolvedRegionalOverlays = this.resolveRegionalOverlays();
|
||||||
this.biomesResolved = true;
|
this.biomesResolved = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,6 +140,10 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
|||||||
return this.umbralHillsMaxY;
|
return this.umbralHillsMaxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<RegionalBiomeOverlay> regionalOverlays() {
|
||||||
|
return this.regionalOverlays;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MapCodec<? extends BiomeSource> codec() {
|
protected MapCodec<? extends BiomeSource> codec() {
|
||||||
return CODEC;
|
return CODEC;
|
||||||
@@ -179,6 +190,12 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
|||||||
return this.umbralHillsBiome;
|
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);
|
Holder<Biome> delegateBiome = this.delegate.getNoiseBiome(x, y, z, sampler);
|
||||||
return this.nightmareWastesBiome != null ? this.nightmareWastesBiome : delegateBiome;
|
return this.nightmareWastesBiome != null ? this.nightmareWastesBiome : delegateBiome;
|
||||||
}
|
}
|
||||||
@@ -198,6 +215,37 @@ public final class ThoraxisBiomeSource extends BiomeSource {
|
|||||||
.orElse(null);
|
.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) {
|
private double cachedRegionNoise(int quartX, int quartZ) {
|
||||||
long key = net.minecraft.world.level.ChunkPos.asLong(quartX, quartZ);
|
long key = net.minecraft.world.level.ChunkPos.asLong(quartX, quartZ);
|
||||||
Long2DoubleOpenHashMap cache = REGION_CACHE.get();
|
Long2DoubleOpenHashMap cache = REGION_CACHE.get();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.craisinlord.antarchy.mixins;
|
package com.craisinlord.antarchy.mixins;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.renderer.SodiumCompat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
@@ -28,6 +29,17 @@ public final class AntarchyMixinPlugin implements IMixinConfigPlugin {
|
|||||||
if (!classPresent(targetClassName)) {
|
if (!classPresent(targetClassName)) {
|
||||||
return false;
|
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.")) {
|
if (mixinClassName.startsWith("com.craisinlord.antarchy.mixins.compat.scguns.")) {
|
||||||
return classPresent("top.ribs.scguns.entity.projectile.ProjectileEntity");
|
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;
|
package com.craisinlord.antarchy.mixins;
|
||||||
|
|
||||||
import com.craisinlord.antarchy.content.item.InnateEnchantmentHelper;
|
import com.craisinlord.antarchy.content.item.InnateEnchantmentHelper;
|
||||||
|
import com.craisinlord.antarchy.content.item.GiantFryingPanItem;
|
||||||
import net.minecraft.world.inventory.GrindstoneMenu;
|
import net.minecraft.world.inventory.GrindstoneMenu;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -19,6 +20,11 @@ public abstract class GrindstoneMenuMixin {
|
|||||||
|
|
||||||
@Inject(method = "computeResult", at = @At("RETURN"), cancellable = true)
|
@Inject(method = "computeResult", at = @At("RETURN"), cancellable = true)
|
||||||
private void antarchy$blockInnateOnlyNoOp(ItemStack firstInput, ItemStack secondInput, CallbackInfoReturnable<ItemStack> cir) {
|
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()
|
if (!firstInput.isEmpty() && secondInput.isEmpty()
|
||||||
&& InnateEnchantmentHelper.hasInnateNonCurseEnchantments(firstInput)
|
&& InnateEnchantmentHelper.hasInnateNonCurseEnchantments(firstInput)
|
||||||
&& !InnateEnchantmentHelper.hasRemovableNonCurseEnchantments(firstInput)) {
|
&& !InnateEnchantmentHelper.hasRemovableNonCurseEnchantments(firstInput)) {
|
||||||
|
|||||||
+29
-2
@@ -1,7 +1,7 @@
|
|||||||
package com.craisinlord.antarchy.mixins;
|
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.PortalGunCollisionHelper;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
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.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(Entity.class)
|
@Mixin(Entity.class)
|
||||||
public abstract class PortalGunEntityCollisionMixin {
|
public abstract class PortalGunEntityCollisionMixin {
|
||||||
@Unique
|
@Unique
|
||||||
private AABB antarchy$portalGunStartBox;
|
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"))
|
@Inject(method = "collide", at = @At("HEAD"))
|
||||||
private void antarchy$capturePortalGunStartBox(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
|
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)
|
@Inject(method = "collide", at = @At("RETURN"), cancellable = true)
|
||||||
private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
|
private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
|
||||||
Entity entity = (Entity) (Object) this;
|
Entity entity = (Entity) (Object) this;
|
||||||
if (this.antarchy$portalGunStartBox == null || AntarchyGravityApi.isGravityInverted(entity)) {
|
if (this.antarchy$portalGunStartBox == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue());
|
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue());
|
||||||
cir.setReturnValue(adjusted);
|
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