attack each other and backroom block

This commit is contained in:
2026-03-17 13:28:17 +01:00
parent 1e0df38b03
commit 924c1a1bf8
14 changed files with 639 additions and 6 deletions

View File

@@ -6,7 +6,7 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10
loader_version=0.18.3
# Mod Properties
mod_version=26.3.16.1
mod_version=26.3.17
maven_group=dev.tggamesyt
archives_base_name=szar
# Dependencies

View File

@@ -0,0 +1,18 @@
// AttackEnemyTeamGoal.java
package dev.tggamesyt.szar;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.ActiveTargetGoal;
import net.minecraft.entity.mob.PathAwareEntity;
public class AttackEnemyTeamGoal extends ActiveTargetGoal<LivingEntity> {
public AttackEnemyTeamGoal(PathAwareEntity mob, String myTeam) {
super(mob, LivingEntity.class, true, target -> {
if (target instanceof TeamMember other) {
return !other.getTeam().equals(myTeam);
}
return false;
});
}
}

View File

@@ -1,7 +1,9 @@
package dev.tggamesyt.szar;
import net.minecraft.entity.EntityData;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.ai.goal.WanderAroundFarGoal;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes;
@@ -13,10 +15,13 @@ import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.nbt.NbtString;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class CommunistEntity extends PathAwareEntity implements Arrestable{
public class CommunistEntity extends PathAwareEntity implements Arrestable, TeamMember {
public static boolean arrestable = false;
@Nullable
@@ -31,6 +36,9 @@ public class CommunistEntity extends PathAwareEntity implements Arrestable{
this.goalSelector.add(2, new FollowLeaderWanderGoal(this, 1.0D, 6.0F));
this.goalSelector.add(3, new WanderAroundFarGoal(this, 0.8D));
this.goalSelector.add(1, new AK47AttackGoal(this, 16.0F, 2));
this.targetSelector.add(1, new AggroOnHitRevengeGoal(this));
this.targetSelector.add(2, new AttackEnemyTeamGoal(this, "communist"));
}
@@ -85,5 +93,41 @@ public class CommunistEntity extends PathAwareEntity implements Arrestable{
public StalinEntity getLeader() {
return this.leader;
}
@Override
public String getTeam() {
return "communist";
}
@Override
@Nullable
public EntityData initialize(
ServerWorldAccess world,
LocalDifficulty difficulty,
SpawnReason spawnReason,
@Nullable EntityData entityData,
@Nullable NbtCompound entityNbt
) {
EntityData data = super.initialize(world, difficulty, spawnReason, entityData, entityNbt);
// Only auto-assign if NOT spawned as part of Stalin's group
// (Stalin's group sets the leader manually after this call)
if (this.leader == null && world instanceof ServerWorld serverWorld) {
StalinEntity nearest = serverWorld.getEntitiesByClass(
StalinEntity.class,
this.getBoundingBox().expand(24),
s -> s.isAlive()
).stream()
.min((a, b) -> Double.compare(
a.squaredDistanceTo(this),
b.squaredDistanceTo(this)
))
.orElse(null);
if (nearest != null) {
this.setLeader(nearest);
}
}
return data;
}
}

View File

@@ -25,7 +25,7 @@ import java.util.List;
import static dev.tggamesyt.szar.Szar.NaziEntityType;
public class HitterEntity extends PathAwareEntity implements Arrestable{
public class HitterEntity extends PathAwareEntity implements Arrestable, TeamMember {
public static boolean arrestable = true;
@@ -40,6 +40,7 @@ public class HitterEntity extends PathAwareEntity implements Arrestable{
this.goalSelector.add(3, new LookAroundGoal(this));
this.targetSelector.add(1, new AggroOnHitRevengeGoal(this));
this.targetSelector.add(2, new AttackEnemyTeamGoal(this, "nazi"));
}
@@ -146,5 +147,8 @@ public class HitterEntity extends PathAwareEntity implements Arrestable{
}
}
@Override
public String getTeam() {
return "nazi";
}
}

View File

@@ -1,7 +1,9 @@
package dev.tggamesyt.szar;
import net.minecraft.entity.EntityData;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.ai.goal.WanderAroundFarGoal;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes;
@@ -13,10 +15,13 @@ import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.nbt.NbtString;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class NaziEntity extends PathAwareEntity implements Arrestable{
public class NaziEntity extends PathAwareEntity implements Arrestable, TeamMember {
private boolean hithandPlaying = false;
private int hithandTimer = 0; // ticks remaining
@@ -84,6 +89,9 @@ public class NaziEntity extends PathAwareEntity implements Arrestable{
this.goalSelector.add(2, new FollowLeaderWanderGoal(this, 1.0D, 6.0F));
this.goalSelector.add(3, new WanderAroundFarGoal(this, 0.8D));
this.goalSelector.add(1, new AK47AttackGoal(this, 16.0F, 2));
this.targetSelector.add(1, new AggroOnHitRevengeGoal(this));
this.targetSelector.add(2, new AttackEnemyTeamGoal(this, "nazi"));
}
@@ -138,5 +146,41 @@ public class NaziEntity extends PathAwareEntity implements Arrestable{
public HitterEntity getLeader() {
return this.leader;
}
@Override
public String getTeam() {
return "nazi";
}
@Override
@Nullable
public EntityData initialize(
ServerWorldAccess world,
LocalDifficulty difficulty,
SpawnReason spawnReason,
@Nullable EntityData entityData,
@Nullable NbtCompound entityNbt
) {
EntityData data = super.initialize(world, difficulty, spawnReason, entityData, entityNbt);
// Only auto-assign if NOT spawned as part of Stalin's group
// (Stalin's group sets the leader manually after this call)
if (this.leader == null && world instanceof ServerWorld serverWorld) {
HitterEntity nearest = serverWorld.getEntitiesByClass(
HitterEntity.class,
this.getBoundingBox().expand(24),
s -> s.isAlive()
).stream()
.min((a, b) -> Double.compare(
a.squaredDistanceTo(this),
b.squaredDistanceTo(this)
))
.orElse(null);
if (nearest != null) {
this.setLeader(nearest);
}
}
return data;
}
}

View File

@@ -0,0 +1,252 @@
package dev.tggamesyt.szar;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.World;
import java.util.ArrayList;
import java.util.List;
public class PortalBlock extends Block {
// Cooldown tracker so players don't teleport 20x per second
private static final java.util.Map<java.util.UUID, Long> cooldowns = new java.util.HashMap<>();
public PortalBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getCollisionShape(BlockState state, net.minecraft.world.BlockView world,
BlockPos pos, ShapeContext ctx) {
return VoxelShapes.empty();
}
@Override
public void onEntityCollision(BlockState state, World world, BlockPos pos,
Entity entity) {
if (world.isClient) return;
if (!(entity instanceof ServerPlayerEntity player)) return;
// Cooldown check — 3 seconds
long now = world.getTime();
Long last = cooldowns.get(player.getUuid());
if (last != null && now - last < 60) return;
cooldowns.put(player.getUuid(), now);
// Find the TrackerBlock above (within 5 blocks)
TrackerBlockEntity tracker = findTrackerAbove(world, pos);
if (tracker == null) return;
MinecraftServer server = world.getServer();
if (server == null) return;
if (!tracker.isNetherSide) {
// --- OVERWORLD → NETHER ---
teleportToNether(player, tracker, server, pos);
} else {
// --- NETHER → OVERWORLD ---
teleportToOverworld(player, tracker, server);
}
}
private void teleportToNether(ServerPlayerEntity player, TrackerBlockEntity tracker,
MinecraftServer server, BlockPos portalPos) {
// Save return position (a few blocks above entry)
tracker.returnX = player.getX();
tracker.returnY = player.getY() + 3;
tracker.returnZ = player.getZ();
tracker.markDirty();
// Save inventory
NbtList savedInventory = saveInventory(player);
// Clear inventory
player.getInventory().clear();
// Register player as inside
tracker.addPlayer(player.getUuid());
// Teleport to nether
ServerWorld nether = server.getWorld(World.NETHER);
if (nether == null) return;
double netherX = player.getX();
double netherZ = player.getZ();
double netherY = findSafeY(nether, (int) netherX, (int) netherZ);
// Store saved inventory in player's persistent data
NbtCompound persistent = player.writeNbt(new NbtCompound());
// We use a custom data attachment via the player's nbt
saveInventoryToPlayer(player, savedInventory);
// Store which overworld tracker owns this player
NbtCompound tag = getOrCreateCustomData(player);
tag.putInt("OwnerTrackerX", tracker.getPos().getX());
tag.putInt("OwnerTrackerY", tracker.getPos().getY());
tag.putInt("OwnerTrackerZ", tracker.getPos().getZ());
// Generate nether-side portal structure
BlockPos netherPortalPos = new BlockPos((int) netherX, (int) netherY, (int) netherZ);
generateNetherPortal(nether, netherPortalPos, tracker);
// Teleport
player.teleport(nether, netherX, netherY + 1, netherZ,
player.getYaw(), player.getPitch());
}
private void teleportToOverworld(ServerPlayerEntity player, TrackerBlockEntity tracker,
MinecraftServer server) {
// Restore inventory
restoreInventoryToPlayer(player);
// Remove from nether tracker
tracker.removePlayer(player.getUuid());
// Find overworld paired tracker and remove from that too
ServerWorld overworld = server.getWorld(World.OVERWORLD);
if (overworld != null && tracker.pairedTrackerPos != null) {
if (overworld.getBlockEntity(tracker.pairedTrackerPos)
instanceof TrackerBlockEntity owTracker) {
owTracker.removePlayer(player.getUuid());
// If no players left, remove both trackers and their portal blocks
if (!owTracker.hasPlayers()) {
removePortalStructure(overworld, tracker.pairedTrackerPos);
removePortalStructure((ServerWorld) player.getWorld(), tracker.getPos());
}
}
}
// Teleport back (a few blocks above entry)
player.teleport(overworld,
tracker.returnX, tracker.returnY, tracker.returnZ,
player.getYaw(), player.getPitch());
}
// --- Helpers ---
private TrackerBlockEntity findTrackerAbove(World world, BlockPos portalPos) {
for (int i = 1; i <= 5; i++) {
BlockPos check = portalPos.up(i);
if (world.getBlockState(check).getBlock() instanceof TrackerBlock) {
if (world.getBlockEntity(check) instanceof TrackerBlockEntity te) {
return te;
}
}
}
return null;
}
private double findSafeY(ServerWorld world, int x, int z) {
// Search from y=100 downward for solid ground with 2 air blocks above
for (int y = 100; y > 10; y--) {
BlockPos feet = new BlockPos(x, y, z);
BlockPos head = feet.up();
BlockPos ground = feet.down();
if (!world.getBlockState(feet).isSolidBlock(world, feet)
&& !world.getBlockState(head).isSolidBlock(world, head)
&& world.getBlockState(ground).isSolidBlock(world, ground)) {
return y;
}
}
return 64; // fallback
}
private void generateNetherPortal(ServerWorld nether, BlockPos portalPos,
TrackerBlockEntity overworldTracker) {
// Place TrackerBlock 4 blocks above portal
BlockPos trackerPos = portalPos.up(4);
nether.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState());
if (nether.getBlockEntity(trackerPos) instanceof TrackerBlockEntity netherTracker) {
netherTracker.isNetherSide = true;
netherTracker.returnX = overworldTracker.returnX;
netherTracker.returnY = overworldTracker.returnY;
netherTracker.returnZ = overworldTracker.returnZ;
netherTracker.pairedTrackerPos = overworldTracker.getPos();
overworldTracker.pairedTrackerPos = trackerPos;
overworldTracker.markDirty();
netherTracker.markDirty();
}
// Place portal block at bottom
nether.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState());
}
private void removePortalStructure(ServerWorld world, BlockPos trackerPos) {
// Remove tracker
world.removeBlock(trackerPos, false);
// Remove portal block (4 below)
world.removeBlock(trackerPos.down(4), false);
}
// Inventory persistence via player NBT custom data
private static final String INV_KEY = "SzarSavedInventory";
private NbtList saveInventory(ServerPlayerEntity player) {
NbtList list = new NbtList();
PlayerInventory inv = player.getInventory();
for (int i = 0; i < inv.size(); i++) {
ItemStack stack = inv.getStack(i);
if (!stack.isEmpty()) {
NbtCompound entry = new NbtCompound();
entry.putInt("Slot", i);
stack.writeNbt(entry);
list.add(entry);
}
}
return list;
}
private void saveInventoryToPlayer(ServerPlayerEntity player, NbtList inventory) {
NbtCompound tag = getOrCreateCustomData(player);
tag.put(INV_KEY, inventory);
}
private void restoreInventoryToPlayer(ServerPlayerEntity player) {
NbtCompound tag = getOrCreateCustomData(player);
if (!tag.contains(INV_KEY)) return;
NbtList list = tag.getList(INV_KEY, 10); // 10 = NbtCompound type
player.getInventory().clear();
for (int i = 0; i < list.size(); i++) {
NbtCompound entry = list.getCompound(i);
int slot = entry.getInt("Slot");
ItemStack stack = ItemStack.fromNbt(entry);
player.getInventory().setStack(slot, stack);
}
tag.remove(INV_KEY);
}
// Fabric doesn't have a built-in persistent custom data on players in 1.20.1
// without a mod like FAPI's PersistentStateManager trick.
// The cleanest approach in vanilla Fabric is to store it in a custom
// ServerState attached to the overworld.
private NbtCompound getOrCreateCustomData(ServerPlayerEntity player) {
// We'll use the player's existing nbt stack — store in a sub-tag
// This works for the session but won't survive a crash mid-dimension.
// For a robust solution, use a PersistentState (shown below in PortalDataState.java)
return PortalDataState.getOrCreate(
player.getServer().getWorld(World.OVERWORLD)
).getOrCreatePlayerData(player.getUuid());
}
}

View File

@@ -0,0 +1,46 @@
package dev.tggamesyt.szar;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.PersistentState;
import java.util.UUID;
public class PortalDataState extends PersistentState {
private static final String KEY = "szar_portal_data";
private final NbtCompound data = new NbtCompound();
public NbtCompound getOrCreatePlayerData(UUID uuid) {
String key = uuid.toString();
if (!data.contains(key)) {
data.put(key, new NbtCompound());
}
return data.getCompound(key);
}
public void removePlayerData(UUID uuid) {
data.remove(uuid.toString());
markDirty();
}
@Override
public NbtCompound writeNbt(NbtCompound nbt) {
nbt.put("PlayerData", data.copy());
return nbt;
}
public static PortalDataState fromNbt(NbtCompound nbt) {
PortalDataState state = new PortalDataState();
NbtCompound saved = nbt.getCompound("PlayerData");
for (String key : saved.getKeys()) {
state.data.put(key, saved.getCompound(key));
}
return state;
}
public static PortalDataState getOrCreate(ServerWorld overworld) {
return overworld.getPersistentStateManager()
.getOrCreate(PortalDataState::fromNbt, PortalDataState::new, KEY);
}
}

View File

@@ -26,7 +26,7 @@ import java.util.List;
import static dev.tggamesyt.szar.Szar.CommunistEntityType;
public class StalinEntity extends PathAwareEntity implements Arrestable{
public class StalinEntity extends PathAwareEntity implements Arrestable, TeamMember {
public static boolean arrestable = true;
@@ -41,6 +41,7 @@ public class StalinEntity extends PathAwareEntity implements Arrestable{
this.goalSelector.add(3, new LookAroundGoal(this));
this.targetSelector.add(1, new AggroOnHitRevengeGoal(this));
this.targetSelector.add(2, new AttackEnemyTeamGoal(this, "communist"));
}
@@ -148,4 +149,8 @@ public class StalinEntity extends PathAwareEntity implements Arrestable{
}
@Override
public String getTeam() {
return "communist";
}
}

View File

@@ -1065,6 +1065,38 @@ public class Szar implements ModInitializer {
});
});
}
// Blocks
public static final TrackerBlock TRACKER_BLOCK = Registry.register(
Registries.BLOCK, new Identifier(MOD_ID, "tracker"),
new TrackerBlock(FabricBlockSettings.create().noCollision().air())
// .air() makes it not render and not block light
);
public static final PortalBlock PORTAL_BLOCK = Registry.register(
Registries.BLOCK, new Identifier(MOD_ID, "portal"),
new PortalBlock(FabricBlockSettings.create().noCollision()
.strength(-1.0f) // indestructible by default, change if needed
.luminance(state -> 11)) // slight glow so you can see it
);
// Block items (so you can place them)
public static final BlockItem TRACKER_BLOCK_ITEM = Registry.register(
Registries.ITEM, new Identifier(MOD_ID, "tracker"),
new BlockItem(TRACKER_BLOCK, new FabricItemSettings())
);
public static final BlockItem PORTAL_BLOCK_ITEM = Registry.register(
Registries.ITEM, new Identifier(MOD_ID, "portal"),
new BlockItem(PORTAL_BLOCK, new FabricItemSettings())
);
// Block entity
public static final BlockEntityType<TrackerBlockEntity> TRACKER_BLOCK_ENTITY =
Registry.register(
Registries.BLOCK_ENTITY_TYPE,
new Identifier(MOD_ID, "tracker"),
FabricBlockEntityTypeBuilder.create(TrackerBlockEntity::new, TRACKER_BLOCK).build()
);
// In your ModItems or wherever you register items
public static final Item REVOLVER = Registry.register(

View File

@@ -0,0 +1,5 @@
package dev.tggamesyt.szar;
public interface TeamMember {
String getTeam(); // returns "communist" or "tsarist" (or whatever your 2nd team is)
}

View File

@@ -0,0 +1,52 @@
package dev.tggamesyt.szar;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class TrackerBlock extends Block implements BlockEntityProvider {
public TrackerBlock(Settings settings) {
super(settings);
}
// No hitbox
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world,
BlockPos pos, ShapeContext ctx) {
return VoxelShapes.empty();
}
// No collision
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView world,
BlockPos pos, ShapeContext ctx) {
return VoxelShapes.empty();
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new TrackerBlockEntity(pos, state);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state,
@Nullable LivingEntity placer, ItemStack itemStack) {
if (world.isClient) return;
BlockPos portalPos = pos.down(4);
world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState());
}
}

View File

@@ -0,0 +1,91 @@
package dev.tggamesyt.szar;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.nbt.NbtString;
import net.minecraft.util.math.BlockPos;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class TrackerBlockEntity extends BlockEntity {
// The overworld entry coords (used by nether-side tracker to know where to send players back)
public double returnX, returnY, returnZ;
// Whether this tracker is in the nether or overworld
public boolean isNetherSide = false;
// BlockPos of the paired tracker in the other dimension
public BlockPos pairedTrackerPos = null;
// UUIDs of players currently inside the dimension via this portal
private final Set<UUID> playersInside = new HashSet<>();
public TrackerBlockEntity(BlockPos pos, BlockState state) {
super(Szar.TRACKER_BLOCK_ENTITY, pos, state);
}
public void addPlayer(UUID uuid) {
playersInside.add(uuid);
markDirty();
}
public void removePlayer(UUID uuid) {
playersInside.remove(uuid);
markDirty();
}
public boolean hasPlayers() {
return !playersInside.isEmpty();
}
public Set<UUID> getPlayersInside() {
return playersInside;
}
@Override
public void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);
nbt.putDouble("ReturnX", returnX);
nbt.putDouble("ReturnY", returnY);
nbt.putDouble("ReturnZ", returnZ);
nbt.putBoolean("IsNetherSide", isNetherSide);
if (pairedTrackerPos != null) {
nbt.putInt("PairedX", pairedTrackerPos.getX());
nbt.putInt("PairedY", pairedTrackerPos.getY());
nbt.putInt("PairedZ", pairedTrackerPos.getZ());
}
NbtList list = new NbtList();
for (UUID uuid : playersInside) {
list.add(NbtString.of(uuid.toString()));
}
nbt.put("PlayersInside", list);
}
@Override
public void readNbt(NbtCompound nbt) {
super.readNbt(nbt);
returnX = nbt.getDouble("ReturnX");
returnY = nbt.getDouble("ReturnY");
returnZ = nbt.getDouble("ReturnZ");
isNetherSide = nbt.getBoolean("IsNetherSide");
if (nbt.contains("PairedX")) {
pairedTrackerPos = new BlockPos(
nbt.getInt("PairedX"),
nbt.getInt("PairedY"),
nbt.getInt("PairedZ")
);
}
NbtList list = nbt.getList("PlayersInside", 8);
playersInside.clear();
for (int i = 0; i < list.size(); i++) {
playersInside.add(UUID.fromString(list.getString(i)));
}
}
}

View File

@@ -0,0 +1,39 @@
package dev.tggamesyt.szar.mixin;
import dev.tggamesyt.szar.TrackerBlock;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.EntityShapeContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
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(AbstractBlock.AbstractBlockState.class)
public class NoClipMixin {
@Inject(method = "getCollisionShape*", at = @At("HEAD"), cancellable = true)
private void szar_noClipBelowTracker(BlockView world, BlockPos pos,
ShapeContext ctx, CallbackInfoReturnable<VoxelShape> cir) {
// Only applies to players
if (!(ctx instanceof EntityShapeContext esc)) return;
Entity entity = esc.getEntity();
if (!(entity instanceof PlayerEntity)) return;
// Check 15 blocks above this position for a TrackerBlock
for (int i = 1; i <= 5; i++) {
BlockPos above = pos.up(i);
if (world.getBlockState(above).getBlock() instanceof TrackerBlock) {
cir.setReturnValue(VoxelShapes.empty());
return;
}
}
}
}

View File

@@ -7,6 +7,7 @@
"CraftingScreenHandlerMixin",
"CraftingScreenHandlerMixin2",
"LivingEntityFallDamageMixin",
"NoClipMixin",
"PlaneBlockInteractionMixin",
"PlayerEntityMixin",
"PlayerInteractionMixin",