attack each other and backroom block
This commit is contained in:
@@ -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
|
||||
|
||||
18
src/main/java/dev/tggamesyt/szar/AttackEnemyTeamGoal.java
Normal file
18
src/main/java/dev/tggamesyt/szar/AttackEnemyTeamGoal.java
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
252
src/main/java/dev/tggamesyt/szar/PortalBlock.java
Normal file
252
src/main/java/dev/tggamesyt/szar/PortalBlock.java
Normal 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());
|
||||
}
|
||||
}
|
||||
46
src/main/java/dev/tggamesyt/szar/PortalDataState.java
Normal file
46
src/main/java/dev/tggamesyt/szar/PortalDataState.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
5
src/main/java/dev/tggamesyt/szar/TeamMember.java
Normal file
5
src/main/java/dev/tggamesyt/szar/TeamMember.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
public interface TeamMember {
|
||||
String getTeam(); // returns "communist" or "tsarist" (or whatever your 2nd team is)
|
||||
}
|
||||
52
src/main/java/dev/tggamesyt/szar/TrackerBlock.java
Normal file
52
src/main/java/dev/tggamesyt/szar/TrackerBlock.java
Normal 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());
|
||||
}
|
||||
}
|
||||
91
src/main/java/dev/tggamesyt/szar/TrackerBlockEntity.java
Normal file
91
src/main/java/dev/tggamesyt/szar/TrackerBlockEntity.java
Normal 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/main/java/dev/tggamesyt/szar/mixin/NoClipMixin.java
Normal file
39
src/main/java/dev/tggamesyt/szar/mixin/NoClipMixin.java
Normal 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 1–5 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"CraftingScreenHandlerMixin",
|
||||
"CraftingScreenHandlerMixin2",
|
||||
"LivingEntityFallDamageMixin",
|
||||
"NoClipMixin",
|
||||
"PlaneBlockInteractionMixin",
|
||||
"PlayerEntityMixin",
|
||||
"PlayerInteractionMixin",
|
||||
|
||||
Reference in New Issue
Block a user