backrooms 3
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.18.3
|
||||
mod_version=26.3.19
|
||||
maven_group=dev.tggamesyt
|
||||
archives_base_name=szar
|
||||
# Dependencies
|
||||
|
||||
108
src/main/java/dev/tggamesyt/szar/AlmondWaterItem.java
Normal file
108
src/main/java/dev/tggamesyt/szar/AlmondWaterItem.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.minecraft.advancement.criterion.Criteria;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||
import net.minecraft.entity.effect.StatusEffectCategory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsage;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.stat.Stats;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.UseAction;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class AlmondWaterItem extends Item {
|
||||
|
||||
public AlmondWaterItem(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
|
||||
PlayerEntity player = user instanceof PlayerEntity ? (PlayerEntity) user : null;
|
||||
|
||||
// Advancement trigger
|
||||
if (player instanceof ServerPlayerEntity serverPlayer) {
|
||||
Criteria.CONSUME_ITEM.trigger(serverPlayer, stack);
|
||||
}
|
||||
|
||||
if (!world.isClient) {
|
||||
// Collect harmful effects first
|
||||
List<StatusEffectInstance> toRemove = new ArrayList<>();
|
||||
|
||||
for (StatusEffectInstance effect : user.getStatusEffects()) {
|
||||
if (effect.getEffectType().getCategory() == StatusEffectCategory.HARMFUL) {
|
||||
toRemove.add(effect);
|
||||
}
|
||||
}
|
||||
|
||||
// Then remove them
|
||||
for (StatusEffectInstance effect : toRemove) {
|
||||
user.removeStatusEffect(effect.getEffectType());
|
||||
}
|
||||
|
||||
// Hunger + saturation
|
||||
if (player != null) {
|
||||
player.getHungerManager().add(4, 0.6F);
|
||||
}
|
||||
}
|
||||
|
||||
// Stats + stack handling
|
||||
if (player != null) {
|
||||
player.incrementStat(Stats.USED.getOrCreateStat(this));
|
||||
if (!player.getAbilities().creativeMode) {
|
||||
stack.decrement(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Return glass bottle
|
||||
if (player == null || !player.getAbilities().creativeMode) {
|
||||
if (stack.isEmpty()) {
|
||||
return new ItemStack(Items.GLASS_BOTTLE);
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
player.getInventory().insertStack(new ItemStack(Items.GLASS_BOTTLE));
|
||||
}
|
||||
}
|
||||
|
||||
user.emitGameEvent(net.minecraft.world.event.GameEvent.DRINK);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxUseTime(ItemStack stack) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UseAction getUseAction(ItemStack stack) {
|
||||
return UseAction.DRINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoundEvent getDrinkSound() {
|
||||
return SoundEvents.ENTITY_GENERIC_DRINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoundEvent getEatSound() {
|
||||
return SoundEvents.ENTITY_GENERIC_DRINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
||||
return ItemUsage.consumeHeldItem(world, user, hand);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package dev.tggamesyt.szar;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.block.entity.BarrelBlockEntity;
|
||||
import net.minecraft.entity.ItemEntity;
|
||||
import net.minecraft.entity.player.HungerManager;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -22,6 +23,16 @@ public class BackroomsBarrelManager {
|
||||
private static final Map<UUID, Set<BlockPos>> trackerBarrels = new HashMap<>();
|
||||
private static final Map<UUID, Set<BlockPos>> foodBarrels = new HashMap<>();
|
||||
|
||||
// Cooldown: world time when food was last taken from a barrel per player
|
||||
private static final Map<UUID, Long> foodTakenCooldown = new HashMap<>();
|
||||
|
||||
// How far from ANY player a barrel must be to get cleared
|
||||
private static final int CLEAR_RANGE = 32;
|
||||
// Cooldown in ticks (20 ticks/sec * 60 sec = 1200)
|
||||
private static final long FOOD_COOLDOWN_TICKS = 1200;
|
||||
// Range to check for dropped food items on ground
|
||||
private static final int DROPPED_FOOD_RANGE = 8;
|
||||
|
||||
public static void register() {
|
||||
ServerTickEvents.END_SERVER_TICK.register(BackroomsBarrelManager::tick);
|
||||
}
|
||||
@@ -30,7 +41,13 @@ public class BackroomsBarrelManager {
|
||||
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) return;
|
||||
|
||||
for (ServerPlayerEntity player : backrooms.getPlayers()) {
|
||||
List<ServerPlayerEntity> players = backrooms.getPlayers();
|
||||
|
||||
// --- Clear barrels too far from any player ---
|
||||
clearDistantBarrels(backrooms, players, trackerBarrels, Szar.TRACKER_BLOCK_ITEM.asItem());
|
||||
clearDistantBarrels(backrooms, players, foodBarrels, null);
|
||||
|
||||
for (ServerPlayerEntity player : players) {
|
||||
UUID uuid = player.getUuid();
|
||||
|
||||
// --- Walk tracking ---
|
||||
@@ -52,28 +69,71 @@ public class BackroomsBarrelManager {
|
||||
|
||||
// --- Check if tracker barrels need clearing ---
|
||||
if (trackerBarrels.containsKey(uuid)) {
|
||||
checkAndClearTrackerBarrels(backrooms, uuid);
|
||||
checkAndClearBarrels(backrooms, uuid, trackerBarrels,
|
||||
Szar.TRACKER_BLOCK_ITEM.asItem());
|
||||
}
|
||||
|
||||
// --- Check if food barrels need clearing ---
|
||||
if (foodBarrels.containsKey(uuid)) {
|
||||
checkAndClearFoodBarrels(backrooms, uuid);
|
||||
boolean anyTaken = checkFoodBarrelsTaken(backrooms, uuid);
|
||||
if (anyTaken) {
|
||||
// Clear all food from tracked barrels and start cooldown
|
||||
clearAllFoodBarrels(backrooms, uuid);
|
||||
foodTakenCooldown.put(uuid, backrooms.getTime());
|
||||
foodBarrels.remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Hunger check (every 20 ticks) ---
|
||||
if (backrooms.getTime() % 20 == 0) {
|
||||
HungerManager hunger = player.getHungerManager();
|
||||
if (hunger.getFoodLevel() <= 10 && !hasAnyFood(player)) {
|
||||
if (!foodBarrels.containsKey(uuid)) {
|
||||
List<BarrelBlockEntity> nearby = getNearbyBarrels(backrooms, player, 16);
|
||||
if (!nearby.isEmpty()) {
|
||||
Set<BlockPos> positions = new HashSet<>();
|
||||
for (BarrelBlockEntity barrel : nearby) {
|
||||
placeItemInBarrel(barrel, new ItemStack(Szar.CAN_OF_BEANS));
|
||||
positions.add(barrel.getPos().toImmutable());
|
||||
}
|
||||
foodBarrels.put(uuid, positions);
|
||||
boolean isHungry = hunger.getFoodLevel() <= 10;
|
||||
boolean hasFood = hasAnyFood(player);
|
||||
boolean hasFoodOnGround = hasFoodDroppedNearby(backrooms, player);
|
||||
long lastTaken = foodTakenCooldown.getOrDefault(uuid, -FOOD_COOLDOWN_TICKS);
|
||||
boolean onCooldown = (backrooms.getTime() - lastTaken) < FOOD_COOLDOWN_TICKS;
|
||||
|
||||
if (isHungry && !hasFood && !hasFoodOnGround && !onCooldown) {
|
||||
// Ensure ALL nearby barrels have food, not just new ones
|
||||
List<BarrelBlockEntity> nearby = getNearbyBarrels(backrooms, player, 16);
|
||||
Set<BlockPos> positions = foodBarrels.getOrDefault(uuid, new HashSet<>());
|
||||
|
||||
for (BarrelBlockEntity barrel : nearby) {
|
||||
BlockPos bpos = barrel.getPos().toImmutable();
|
||||
// If this barrel doesn't have food yet, add it
|
||||
if (!barrelHasItem(barrel, Szar.CAN_OF_BEANS.asItem())
|
||||
&& !barrelHasItem(barrel, Szar.ALMOND_WATER.asItem())) {
|
||||
Item foodItem = backrooms.random.nextBoolean()
|
||||
? Szar.CAN_OF_BEANS.asItem()
|
||||
: Szar.ALMOND_WATER.asItem();
|
||||
placeItemInBarrel(barrel, new ItemStack(foodItem));
|
||||
}
|
||||
positions.add(bpos);
|
||||
}
|
||||
|
||||
// Also clear positions that are now out of range
|
||||
positions.removeIf(bpos -> {
|
||||
double d = player.squaredDistanceTo(
|
||||
bpos.getX(), bpos.getY(), bpos.getZ());
|
||||
if (d > 16 * 16) {
|
||||
if (backrooms.getBlockEntity(bpos)
|
||||
instanceof BarrelBlockEntity b) {
|
||||
removeItemFromBarrel(b, Szar.CAN_OF_BEANS.asItem());
|
||||
removeItemFromBarrel(b, Szar.ALMOND_WATER.asItem());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!positions.isEmpty()) {
|
||||
foodBarrels.put(uuid, positions);
|
||||
}
|
||||
} else if (onCooldown || hasFood || hasFoodOnGround) {
|
||||
// If player now has food or is on cooldown, clear all food barrels
|
||||
if (foodBarrels.containsKey(uuid)) {
|
||||
clearAllFoodBarrels(backrooms, uuid);
|
||||
foodBarrels.remove(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,47 +157,98 @@ public class BackroomsBarrelManager {
|
||||
|
||||
// Clean up data for players no longer in backrooms
|
||||
Set<UUID> activePlayers = new HashSet<>();
|
||||
for (ServerPlayerEntity p : backrooms.getPlayers()) activePlayers.add(p.getUuid());
|
||||
for (ServerPlayerEntity p : players) activePlayers.add(p.getUuid());
|
||||
lastX.keySet().retainAll(activePlayers);
|
||||
lastZ.keySet().retainAll(activePlayers);
|
||||
walkAccumulator.keySet().retainAll(activePlayers);
|
||||
walkThreshold.keySet().retainAll(activePlayers);
|
||||
foodBarrels.keySet().retainAll(activePlayers);
|
||||
trackerBarrels.keySet().retainAll(activePlayers);
|
||||
foodTakenCooldown.keySet().retainAll(activePlayers);
|
||||
}
|
||||
|
||||
private static void checkAndClearTrackerBarrels(ServerWorld world, UUID uuid) {
|
||||
Set<BlockPos> positions = trackerBarrels.get(uuid);
|
||||
if (positions == null) return;
|
||||
|
||||
boolean anyTaken = false;
|
||||
private static boolean checkFoodBarrelsTaken(ServerWorld world, UUID uuid) {
|
||||
Set<BlockPos> positions = foodBarrels.get(uuid);
|
||||
if (positions == null) return false;
|
||||
for (BlockPos pos : positions) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
if (!barrelHasItem(barrel, Szar.TRACKER_BLOCK_ITEM.asItem())) {
|
||||
anyTaken = true;
|
||||
break;
|
||||
if (!barrelHasItem(barrel, Szar.CAN_OF_BEANS.asItem())
|
||||
&& !barrelHasItem(barrel, Szar.ALMOND_WATER.asItem())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyTaken) {
|
||||
for (BlockPos pos : positions) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
removeItemFromBarrel(barrel, Szar.TRACKER_BLOCK_ITEM.asItem());
|
||||
}
|
||||
}
|
||||
trackerBarrels.remove(uuid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void checkAndClearFoodBarrels(ServerWorld world, UUID uuid) {
|
||||
private static void clearAllFoodBarrels(ServerWorld world, UUID uuid) {
|
||||
Set<BlockPos> positions = foodBarrels.get(uuid);
|
||||
if (positions == null) return;
|
||||
for (BlockPos pos : positions) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
removeItemFromBarrel(barrel, Szar.CAN_OF_BEANS.asItem());
|
||||
removeItemFromBarrel(barrel, Szar.ALMOND_WATER.asItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasFoodDroppedNearby(ServerWorld world, ServerPlayerEntity player) {
|
||||
Box box = player.getBoundingBox().expand(DROPPED_FOOD_RANGE);
|
||||
List<ItemEntity> items = world.getEntitiesByClass(ItemEntity.class, box, e -> {
|
||||
ItemStack stack = e.getStack();
|
||||
return stack.isFood()
|
||||
|| stack.isOf(Szar.CAN_OF_BEANS)
|
||||
|| stack.isOf(Szar.ALMOND_WATER);
|
||||
});
|
||||
return !items.isEmpty();
|
||||
}
|
||||
|
||||
private static void clearDistantBarrels(ServerWorld world,
|
||||
List<ServerPlayerEntity> players,
|
||||
Map<UUID, Set<BlockPos>> barrelMap,
|
||||
Item item) {
|
||||
Iterator<Map.Entry<UUID, Set<BlockPos>>> iter = barrelMap.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry<UUID, Set<BlockPos>> entry = iter.next();
|
||||
Set<BlockPos> positions = entry.getValue();
|
||||
|
||||
boolean allDistant = true;
|
||||
for (BlockPos pos : positions) {
|
||||
for (ServerPlayerEntity player : players) {
|
||||
if (player.squaredDistanceTo(pos.getX(), pos.getY(), pos.getZ())
|
||||
<= CLEAR_RANGE * CLEAR_RANGE) {
|
||||
allDistant = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allDistant) break;
|
||||
}
|
||||
|
||||
if (allDistant) {
|
||||
for (BlockPos pos : positions) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
if (item != null) {
|
||||
removeItemFromBarrel(barrel, item);
|
||||
} else {
|
||||
removeItemFromBarrel(barrel, Szar.CAN_OF_BEANS.asItem());
|
||||
removeItemFromBarrel(barrel, Szar.ALMOND_WATER.asItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAndClearBarrels(ServerWorld world, UUID uuid,
|
||||
Map<UUID, Set<BlockPos>> barrelMap, Item item) {
|
||||
Set<BlockPos> positions = barrelMap.get(uuid);
|
||||
if (positions == null) return;
|
||||
|
||||
boolean anyTaken = false;
|
||||
for (BlockPos pos : positions) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
if (!barrelHasItem(barrel, Szar.CAN_OF_BEANS.asItem())) {
|
||||
if (!barrelHasItem(barrel, item)) {
|
||||
anyTaken = true;
|
||||
break;
|
||||
}
|
||||
@@ -147,10 +258,10 @@ public class BackroomsBarrelManager {
|
||||
if (anyTaken) {
|
||||
for (BlockPos pos : positions) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
removeItemFromBarrel(barrel, Szar.CAN_OF_BEANS.asItem());
|
||||
removeItemFromBarrel(barrel, item);
|
||||
}
|
||||
}
|
||||
foodBarrels.remove(uuid);
|
||||
barrelMap.remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +286,8 @@ public class BackroomsBarrelManager {
|
||||
for (int i = 0; i < player.getInventory().size(); i++) {
|
||||
ItemStack stack = player.getInventory().getStack(i);
|
||||
if (!stack.isEmpty() && (stack.isFood()
|
||||
|| stack.isOf(Szar.CAN_OF_BEANS))) {
|
||||
|| stack.isOf(Szar.CAN_OF_BEANS)
|
||||
|| stack.isOf(Szar.ALMOND_WATER))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -183,13 +295,14 @@ public class BackroomsBarrelManager {
|
||||
}
|
||||
|
||||
private static void placeItemInBarrel(BarrelBlockEntity barrel, ItemStack item) {
|
||||
List<Integer> emptySlots = new ArrayList<>();
|
||||
for (int i = 0; i < barrel.size(); i++) {
|
||||
if (barrel.getStack(i).isEmpty()) {
|
||||
barrel.setStack(i, item.copy());
|
||||
barrel.markDirty();
|
||||
return;
|
||||
}
|
||||
if (barrel.getStack(i).isEmpty()) emptySlots.add(i);
|
||||
}
|
||||
if (emptySlots.isEmpty()) return;
|
||||
int slot = emptySlots.get((int)(Math.random() * emptySlots.size()));
|
||||
barrel.setStack(slot, item.copy());
|
||||
barrel.markDirty();
|
||||
}
|
||||
|
||||
private static List<BarrelBlockEntity> getNearbyBarrels(ServerWorld world,
|
||||
@@ -197,10 +310,8 @@ public class BackroomsBarrelManager {
|
||||
int radius) {
|
||||
List<BarrelBlockEntity> result = new ArrayList<>();
|
||||
Box box = player.getBoundingBox().expand(radius);
|
||||
|
||||
BlockPos min = BlockPos.ofFloored(box.minX, box.minY, box.minZ);
|
||||
BlockPos max = BlockPos.ofFloored(box.maxX, box.maxY, box.maxZ);
|
||||
|
||||
for (BlockPos pos : BlockPos.iterate(min, max)) {
|
||||
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
|
||||
result.add(barrel);
|
||||
|
||||
@@ -87,11 +87,28 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
|
||||
Szar.PLASTIC.getDefaultState(), false);
|
||||
}
|
||||
|
||||
// Ceiling
|
||||
boolean isGlowstone = isGlowstonePos(worldX, worldZ);
|
||||
BlockState ceilingBlock = isGlowstone
|
||||
? Blocks.GLOWSTONE.getDefaultState()
|
||||
: Szar.CEILING.getDefaultState();
|
||||
BlockState ceilingBlock;
|
||||
if (isGlowstone) {
|
||||
long lightRoll = hash(worldX * 53 + 7, worldZ * 47 + 13);
|
||||
int roll = (int)(Math.abs(lightRoll) % 100);
|
||||
if (roll < 95) {
|
||||
// 95% ON
|
||||
ceilingBlock = Szar.BACKROOMS_LIGHT.getDefaultState()
|
||||
.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.ON);
|
||||
} else if (roll < 98) {
|
||||
// 3% FLICKERING_ON
|
||||
ceilingBlock = Szar.BACKROOMS_LIGHT.getDefaultState()
|
||||
.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.FLICKERING_ON);
|
||||
} else {
|
||||
// 2% missing — ceiling block, no light
|
||||
ceilingBlock = Szar.CEILING.getDefaultState();
|
||||
}
|
||||
} else {
|
||||
ceilingBlock = Szar.CEILING.getDefaultState();
|
||||
}
|
||||
chunk.setBlockState(new BlockPos(lx, CEILING_Y, lz), ceilingBlock, false);
|
||||
|
||||
// Above ceiling — solid wall block fill so there's no void above
|
||||
@@ -106,10 +123,17 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
|
||||
Blocks.AIR.getDefaultState(), false);
|
||||
}
|
||||
|
||||
// 1 in 40 chance of a barrel on the floor
|
||||
// With this — 1 per ~40x40 block area:
|
||||
long barrelHash = hash(worldX * 31 + 17, worldZ * 29 + 11);
|
||||
if ((barrelHash % 1600) == 0) {
|
||||
int cellX = Math.floorDiv(worldX, 40);
|
||||
int cellZ = Math.floorDiv(worldZ, 40);
|
||||
long cellHash = hash(cellX * 31 + 17, cellZ * 29 + 11);
|
||||
|
||||
int barrelLocalX = (int)(cellHash & 0x1F) % 40; // 0-39
|
||||
int barrelLocalZ = (int)((cellHash >> 8) & 0x1F) % 40; // 0-39
|
||||
int cellStartX = cellX * 40;
|
||||
int cellStartZ = cellZ * 40;
|
||||
|
||||
if (worldX == cellStartX + barrelLocalX && worldZ == cellStartZ + barrelLocalZ
|
||||
&& isOpenSpace(worldX, worldZ)) {
|
||||
chunk.setBlockState(new BlockPos(lx, FLOOR_Y + 1, lz),
|
||||
Blocks.BARREL.getDefaultState(), false);
|
||||
}
|
||||
@@ -262,6 +286,19 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Inside the lx/lz loop in generateFeatures, after the wall block section:
|
||||
mutable.set(chunkX + lx, 9, chunkZ + lz); // CEILING_Y = 9
|
||||
BlockState lightState = world.getBlockState(mutable);
|
||||
if (lightState.getBlock() instanceof BackroomsLightBlock) {
|
||||
if (world.getBlockEntity(mutable) == null) {
|
||||
world.setBlockState(mutable, lightState, Block.NOTIFY_ALL);
|
||||
}
|
||||
if (world.getBlockEntity(mutable) instanceof BackroomsLightBlockEntity light) {
|
||||
light.flickerOffset = (int)(Math.abs(hash(chunkX + lx, chunkZ + lz)) % 100);
|
||||
light.flickerTimer = light.flickerOffset; // stagger initial timers
|
||||
light.markDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
67
src/main/java/dev/tggamesyt/szar/BackroomsLightBlock.java
Normal file
67
src/main/java/dev/tggamesyt/szar/BackroomsLightBlock.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.EnumProperty;
|
||||
import net.minecraft.util.StringIdentifiable;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BackroomsLightBlock extends BlockWithEntity {
|
||||
|
||||
public enum LightState implements StringIdentifiable {
|
||||
ON, OFF, FLICKERING_ON, FLICKERING_OFF;
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static final EnumProperty<LightState> LIGHT_STATE =
|
||||
EnumProperty.of("light_state", LightState.class);
|
||||
|
||||
public BackroomsLightBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getStateManager().getDefaultState()
|
||||
.with(LIGHT_STATE, LightState.ON));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
builder.add(LIGHT_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRenderType getRenderType(BlockState state) {
|
||||
return BlockRenderType.MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new BackroomsLightBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(
|
||||
World world, BlockState state, BlockEntityType<T> type) {
|
||||
if (world.isClient) return null;
|
||||
return type == Szar.BACKROOMS_LIGHT_ENTITY
|
||||
? (w, pos, s, be) -> BackroomsLightBlockEntity.tick(
|
||||
w, pos, s, (BackroomsLightBlockEntity) be)
|
||||
: null;
|
||||
}
|
||||
|
||||
// Light level based on state
|
||||
public static int getLightLevel(BlockState state) {
|
||||
return switch (state.get(LIGHT_STATE)) {
|
||||
case ON, FLICKERING_ON -> 15;
|
||||
case OFF, FLICKERING_OFF -> 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BackroomsLightBlockEntity extends BlockEntity {
|
||||
|
||||
// Random offset so each light flickers at different times
|
||||
public int flickerOffset = 0;
|
||||
// How many ticks until next state toggle during flicker
|
||||
public int flickerTimer = 0;
|
||||
private boolean initialized = false;
|
||||
|
||||
public BackroomsLightBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(Szar.BACKROOMS_LIGHT_ENTITY, pos, state);
|
||||
}
|
||||
|
||||
public static void tick(World world, BlockPos pos, BlockState state,
|
||||
BackroomsLightBlockEntity entity) {
|
||||
if (!entity.initialized) {
|
||||
entity.flickerOffset = world.random.nextInt(100);
|
||||
entity.initialized = true;
|
||||
entity.markDirty();
|
||||
}
|
||||
|
||||
BackroomsLightManager.tickLight(world, pos, state, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNbt(NbtCompound nbt) {
|
||||
super.writeNbt(nbt);
|
||||
nbt.putInt("FlickerOffset", flickerOffset);
|
||||
nbt.putInt("FlickerTimer", flickerTimer);
|
||||
nbt.putBoolean("Initialized", initialized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound nbt) {
|
||||
super.readNbt(nbt);
|
||||
flickerOffset = nbt.getInt("FlickerOffset");
|
||||
flickerTimer = nbt.getInt("FlickerTimer");
|
||||
initialized = nbt.getBoolean("Initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toInitialChunkDataNbt() {
|
||||
return createNbt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||
return BlockEntityUpdateS2CPacket.create(this);
|
||||
}
|
||||
}
|
||||
213
src/main/java/dev/tggamesyt/szar/BackroomsLightManager.java
Normal file
213
src/main/java/dev/tggamesyt/szar/BackroomsLightManager.java
Normal file
@@ -0,0 +1,213 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BackroomsLightManager {
|
||||
|
||||
// Global event state
|
||||
public enum GlobalEvent { NONE, FLICKER, BLACKOUT }
|
||||
|
||||
public static GlobalEvent currentEvent = GlobalEvent.NONE;
|
||||
public static int eventTimer = 0; // ticks remaining in current event
|
||||
public static int cooldownTimer = 0; // ticks until next event check
|
||||
|
||||
// Flicker event duration: 3-8 seconds
|
||||
private static final int FLICKER_DURATION_MIN = 60;
|
||||
private static final int FLICKER_DURATION_MAX = 160;
|
||||
// Blackout duration: 50-100 seconds
|
||||
private static final int BLACKOUT_MIN = 1000;
|
||||
private static final int BLACKOUT_MAX = 2000;
|
||||
// Check for new event every ~3 minutes
|
||||
private static final int EVENT_COOLDOWN = 3600;
|
||||
|
||||
public static void register() {
|
||||
ServerTickEvents.END_SERVER_TICK.register(BackroomsLightManager::tick);
|
||||
}
|
||||
|
||||
private static void tick(MinecraftServer server) {
|
||||
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) return;
|
||||
|
||||
// Handle event timers
|
||||
if (currentEvent != GlobalEvent.NONE) {
|
||||
eventTimer--;
|
||||
if (eventTimer <= 0) {
|
||||
endEvent(backrooms);
|
||||
}
|
||||
} else {
|
||||
cooldownTimer--;
|
||||
if (cooldownTimer <= 0) {
|
||||
// Roll for new event
|
||||
int roll = backrooms.random.nextInt(100);
|
||||
if (roll < 30) {
|
||||
startBlackout(backrooms);
|
||||
} else if (roll < 63) { // 30% blackout + 33% flicker
|
||||
startFlicker(backrooms);
|
||||
} else {
|
||||
// No event — reset cooldown
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void startFlicker(ServerWorld world) {
|
||||
currentEvent = GlobalEvent.FLICKER;
|
||||
eventTimer = FLICKER_DURATION_MIN + world.random.nextInt(
|
||||
FLICKER_DURATION_MAX - FLICKER_DURATION_MIN);
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
}
|
||||
|
||||
private static void startBlackout(ServerWorld world) {
|
||||
currentEvent = GlobalEvent.BLACKOUT;
|
||||
eventTimer = BLACKOUT_MIN + world.random.nextInt(BLACKOUT_MAX - BLACKOUT_MIN);
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
|
||||
// Immediately turn off all ON lights in loaded chunks
|
||||
setAllLightsOff(world);
|
||||
}
|
||||
|
||||
private static void endEvent(ServerWorld world) {
|
||||
if (currentEvent == GlobalEvent.BLACKOUT) {
|
||||
// Restore all lights
|
||||
setAllLightsOn(world);
|
||||
}
|
||||
currentEvent = GlobalEvent.NONE;
|
||||
eventTimer = 0;
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
}
|
||||
|
||||
private static void setAllLightsOff(ServerWorld world) {
|
||||
forEachLight(world, (pos, state) -> {
|
||||
BackroomsLightBlock.LightState ls = state.get(BackroomsLightBlock.LIGHT_STATE);
|
||||
if (ls == BackroomsLightBlock.LightState.ON) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.OFF));
|
||||
} else if (ls == BackroomsLightBlock.LightState.FLICKERING_ON) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.FLICKERING_OFF));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void setAllLightsOn(ServerWorld world) {
|
||||
forEachLight(world, (pos, state) -> {
|
||||
BackroomsLightBlock.LightState ls = state.get(BackroomsLightBlock.LIGHT_STATE);
|
||||
if (ls == BackroomsLightBlock.LightState.OFF) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.ON));
|
||||
} else if (ls == BackroomsLightBlock.LightState.FLICKERING_OFF) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.FLICKERING_ON));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void forEachLight(ServerWorld world, java.util.function.BiConsumer<BlockPos, BlockState> consumer) {
|
||||
for (net.minecraft.world.chunk.WorldChunk chunk : getLoadedChunks(world)) {
|
||||
BlockPos.Mutable mutable = new BlockPos.Mutable();
|
||||
int cx = chunk.getPos().getStartX();
|
||||
int cz = chunk.getPos().getStartZ();
|
||||
for (int lx = 0; lx < 16; lx++) {
|
||||
for (int lz = 0; lz < 16; lz++) {
|
||||
// Ceiling Y in backrooms is 9
|
||||
mutable.set(cx + lx, 9, cz + lz);
|
||||
BlockState state = world.getBlockState(mutable);
|
||||
if (state.getBlock() instanceof BackroomsLightBlock) {
|
||||
consumer.accept(mutable.toImmutable(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static java.util.List<net.minecraft.world.chunk.WorldChunk> getLoadedChunks(ServerWorld world) {
|
||||
java.util.List<net.minecraft.world.chunk.WorldChunk> chunks = new java.util.ArrayList<>();
|
||||
// Iterate over all players and collect chunks around them
|
||||
for (net.minecraft.server.network.ServerPlayerEntity player : world.getPlayers()) {
|
||||
int playerChunkX = (int) player.getX() >> 4;
|
||||
int playerChunkZ = (int) player.getZ() >> 4;
|
||||
int viewDistance = world.getServer().getPlayerManager().getViewDistance();
|
||||
for (int cx = playerChunkX - viewDistance; cx <= playerChunkX + viewDistance; cx++) {
|
||||
for (int cz = playerChunkZ - viewDistance; cz <= playerChunkZ + viewDistance; cz++) {
|
||||
if (world.getChunkManager().isChunkLoaded(cx, cz)) {
|
||||
net.minecraft.world.chunk.WorldChunk chunk = world.getChunk(cx, cz);
|
||||
if (!chunks.contains(chunk)) {
|
||||
chunks.add(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
// Called per-light from BackroomsLightBlockEntity.tick
|
||||
public static void tickLight(World world, BlockPos pos, BlockState state,
|
||||
BackroomsLightBlockEntity entity) {
|
||||
BackroomsLightBlock.LightState ls = state.get(BackroomsLightBlock.LIGHT_STATE);
|
||||
|
||||
// During blackout, lights are already set off — don't touch them
|
||||
if (currentEvent == GlobalEvent.BLACKOUT) return;
|
||||
|
||||
// Only flickering lights and lights during flicker events need ticking
|
||||
boolean isFlickering = ls == BackroomsLightBlock.LightState.FLICKERING_ON
|
||||
|| ls == BackroomsLightBlock.LightState.FLICKERING_OFF;
|
||||
boolean inFlickerEvent = currentEvent == GlobalEvent.FLICKER;
|
||||
|
||||
if (!isFlickering && !inFlickerEvent) return;
|
||||
|
||||
// Decrement timer
|
||||
entity.flickerTimer--;
|
||||
if (entity.flickerTimer > 0) return;
|
||||
|
||||
// Toggle state and set new random timer
|
||||
// Flickering lights: 2-8 ticks per toggle
|
||||
// Event flicker: same but offset by entity's flickerOffset
|
||||
int baseTime = 2 + world.random.nextInt(7);
|
||||
|
||||
if (inFlickerEvent && !isFlickering) {
|
||||
// Normal ON light during flicker event — toggle it
|
||||
if (ls == BackroomsLightBlock.LightState.ON) {
|
||||
// Apply offset so not all lights flicker simultaneously
|
||||
if (entity.flickerTimer == 0 && world.getTime() % 3 == entity.flickerOffset % 3) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.OFF));
|
||||
entity.flickerTimer = baseTime;
|
||||
entity.markDirty();
|
||||
}
|
||||
return;
|
||||
} else if (ls == BackroomsLightBlock.LightState.OFF
|
||||
&& currentEvent == GlobalEvent.FLICKER) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.ON));
|
||||
entity.flickerTimer = baseTime;
|
||||
entity.markDirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFlickering) {
|
||||
BackroomsLightBlock.LightState next =
|
||||
ls == BackroomsLightBlock.LightState.FLICKERING_ON
|
||||
? BackroomsLightBlock.LightState.FLICKERING_OFF
|
||||
: BackroomsLightBlock.LightState.FLICKERING_ON;
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE, next));
|
||||
entity.flickerTimer = baseTime;
|
||||
entity.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public static void forceRestoreAllLights(ServerWorld world) {
|
||||
setAllLightsOn(world);
|
||||
}
|
||||
|
||||
public static void forceBlackout(ServerWorld world) {
|
||||
setAllLightsOff(world);
|
||||
}
|
||||
}
|
||||
@@ -183,21 +183,17 @@ public class PortalBlock extends Block {
|
||||
|
||||
if (owTrackerPos != null && overworld != null) {
|
||||
if (overworld.getBlockEntity(owTrackerPos) instanceof TrackerBlockEntity owTracker) {
|
||||
// Resolve to root of the overworld group
|
||||
TrackerBlockEntity root = owTracker.getRoot(overworld);
|
||||
root.removePlayer(player.getUuid());
|
||||
|
||||
if (!root.hasPlayers()) {
|
||||
// Collect and clean up all connected trackers
|
||||
List<BlockPos> allTrackers = new ArrayList<>();
|
||||
allTrackers.add(root.getPos());
|
||||
for (BlockPos childPortal : root.getControlledPortals()) {
|
||||
allTrackers.add(childPortal.up(4));
|
||||
}
|
||||
|
||||
for (BlockPos trackerPos : allTrackers) {
|
||||
if (overworld.getBlockEntity(trackerPos)
|
||||
instanceof TrackerBlockEntity te) {
|
||||
if (overworld.getBlockEntity(trackerPos) instanceof TrackerBlockEntity te) {
|
||||
TrackerBlock.restoreAndCleanup(overworld, trackerPos, te, server);
|
||||
}
|
||||
}
|
||||
@@ -205,17 +201,95 @@ public class PortalBlock extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up backrooms tracker too
|
||||
if (!netherTracker.hasPlayers() && backrooms != null) {
|
||||
TrackerBlock.restoreAndCleanup(backrooms,
|
||||
netherTracker.getPos(), netherTracker, server);
|
||||
}
|
||||
|
||||
if (overworld == null) return;
|
||||
player.teleport(overworld, returnX, returnY, returnZ,
|
||||
|
||||
// Find a safe landing spot — try entry coords first, then spiral, then spawn
|
||||
BlockPos safePos = findSafeOverworldSpot(overworld, returnX, returnY, returnZ, player);
|
||||
player.teleport(overworld,
|
||||
safePos.getX() + 0.5, safePos.getY(), safePos.getZ() + 0.5,
|
||||
player.getYaw(), player.getPitch());
|
||||
}
|
||||
|
||||
private BlockPos findSafeOverworldSpot(ServerWorld world, double baseX, double baseY,
|
||||
double baseZ, ServerPlayerEntity player) {
|
||||
int bx = (int) baseX;
|
||||
int by = (int) baseY;
|
||||
int bz = (int) baseZ;
|
||||
|
||||
// Try exact entry position first
|
||||
BlockPos exact = new BlockPos(bx, by, bz);
|
||||
if (isSafeOverworld(world, exact)) return exact;
|
||||
|
||||
// Try scanning Y up and down from entry Y at exact XZ
|
||||
for (int dy = 0; dy <= 10; dy++) {
|
||||
BlockPos up = new BlockPos(bx, by + dy, bz);
|
||||
if (isSafeOverworld(world, up)) return up;
|
||||
BlockPos down = new BlockPos(bx, by - dy, bz);
|
||||
if (isSafeOverworld(world, down)) return down;
|
||||
}
|
||||
|
||||
// Spiral outward in XZ, scan Y at each spot
|
||||
for (int radius = 1; radius <= 10; radius++) {
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
if (Math.abs(dx) != radius && Math.abs(dz) != radius) continue;
|
||||
int cx = bx + dx;
|
||||
int cz = bz + dz;
|
||||
// Scan Y range at this XZ
|
||||
for (int dy = 0; dy <= 10; dy++) {
|
||||
BlockPos up = new BlockPos(cx, by + dy, cz);
|
||||
if (isSafeOverworld(world, up)) return up;
|
||||
BlockPos down = new BlockPos(cx, by - dy, cz);
|
||||
if (isSafeOverworld(world, down)) return down;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort — use player's spawn point
|
||||
BlockPos spawnPos = player.getSpawnPointPosition();
|
||||
if (spawnPos != null) {
|
||||
// Scan Y near spawn to make sure it's safe
|
||||
for (int dy = 0; dy <= 10; dy++) {
|
||||
BlockPos sp = new BlockPos(spawnPos.getX(), spawnPos.getY() + dy, spawnPos.getZ());
|
||||
if (isSafeOverworld(world, sp)) return sp;
|
||||
}
|
||||
return spawnPos;
|
||||
}
|
||||
|
||||
// Absolute fallback — world spawn
|
||||
BlockPos worldSpawn = world.getSpawnPos();
|
||||
return new BlockPos(worldSpawn.getX(),
|
||||
world.getTopY(net.minecraft.world.Heightmap.Type.MOTION_BLOCKING_NO_LEAVES,
|
||||
worldSpawn.getX(), worldSpawn.getZ()),
|
||||
worldSpawn.getZ());
|
||||
}
|
||||
|
||||
private boolean isSafeOverworld(ServerWorld world, BlockPos feet) {
|
||||
BlockPos head = feet.up();
|
||||
BlockPos ground = feet.down();
|
||||
// Ground must be solid, feet and head must be non-solid and not dangerous
|
||||
if (world.getBlockState(ground).isSolidBlock(world, ground)
|
||||
&& !world.getBlockState(feet).isSolidBlock(world, feet)
|
||||
&& !world.getBlockState(head).isSolidBlock(world, head)) {
|
||||
// Also check not standing in fire, lava, or cactus
|
||||
net.minecraft.block.Block feetBlock = world.getBlockState(feet).getBlock();
|
||||
net.minecraft.block.Block groundBlock = world.getBlockState(ground).getBlock();
|
||||
if (feetBlock == net.minecraft.block.Blocks.LAVA) return false;
|
||||
if (feetBlock == net.minecraft.block.Blocks.FIRE) return false;
|
||||
if (feetBlock == net.minecraft.block.Blocks.CACTUS) return false;
|
||||
if (groundBlock == net.minecraft.block.Blocks.LAVA) return false;
|
||||
if (groundBlock instanceof net.minecraft.block.CactusBlock) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
private TrackerBlockEntity findTrackerAbove(World world, BlockPos portalPos) {
|
||||
|
||||
@@ -55,6 +55,7 @@ import net.minecraft.registry.tag.BiomeTags;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
@@ -370,6 +371,7 @@ public class Szar implements ModInitializer {
|
||||
entries.add(Szar.PLASTIC_ITEM);
|
||||
entries.add(Szar.BEAN);
|
||||
entries.add(Szar.CAN_OF_BEANS);
|
||||
entries.add(Szar.ALMOND_WATER);
|
||||
// crazy weponary
|
||||
entries.add(Szar.BULLET_ITEM);
|
||||
entries.add(Szar.AK47);
|
||||
@@ -970,6 +972,90 @@ public class Szar implements ModInitializer {
|
||||
return 0;
|
||||
})
|
||||
);
|
||||
dispatcher.register(
|
||||
LiteralArgumentBuilder.<ServerCommandSource>literal("backroomlights")
|
||||
.requires(context -> context.hasPermissionLevel(2))
|
||||
.then(CommandManager.literal("get")
|
||||
.executes(context -> {
|
||||
ServerCommandSource source = context.getSource();
|
||||
BackroomsLightManager.GlobalEvent event = BackroomsLightManager.currentEvent;
|
||||
int timer = BackroomsLightManager.eventTimer;
|
||||
|
||||
String mode = switch (event) {
|
||||
case NONE -> "normal";
|
||||
case FLICKER -> "flickering";
|
||||
case BLACKOUT -> "blackout";
|
||||
};
|
||||
|
||||
if (event == BackroomsLightManager.GlobalEvent.NONE) {
|
||||
int cooldown = BackroomsLightManager.cooldownTimer;
|
||||
source.sendMessage(Text.literal(
|
||||
"Current mode: §anormal§r — next event check in §e"
|
||||
+ cooldown + "§r ticks ("
|
||||
+ (cooldown / 20) + "s)"
|
||||
));
|
||||
} else {
|
||||
source.sendMessage(Text.literal(
|
||||
"Current mode: §e" + mode + "§r — ends in §c"
|
||||
+ timer + "§r ticks ("
|
||||
+ (timer / 20) + "s)"
|
||||
));
|
||||
}
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
.then(CommandManager.literal("set")
|
||||
.then(CommandManager.literal("normal")
|
||||
.executes(context -> {
|
||||
ServerCommandSource source = context.getSource();
|
||||
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) {
|
||||
source.sendError(Text.literal("Backrooms dimension not found"));
|
||||
return 0;
|
||||
}
|
||||
// End current event cleanly
|
||||
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.NONE;
|
||||
BackroomsLightManager.eventTimer = 0;
|
||||
BackroomsLightManager.cooldownTimer = 3600;
|
||||
// Restore all lights
|
||||
BackroomsLightManager.forceRestoreAllLights(backrooms);
|
||||
source.sendMessage(Text.literal("§aBackrooms lights set to normal"));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
.then(CommandManager.literal("flickering")
|
||||
.executes(context -> {
|
||||
ServerCommandSource source = context.getSource();
|
||||
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) {
|
||||
source.sendError(Text.literal("Backrooms dimension not found"));
|
||||
return 0;
|
||||
}
|
||||
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.FLICKER;
|
||||
BackroomsLightManager.eventTimer = 3600; // 3 minutes default
|
||||
BackroomsLightManager.cooldownTimer = 3600;
|
||||
source.sendMessage(Text.literal("§eBackrooms lights set to flickering"));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
.then(CommandManager.literal("blackout")
|
||||
.executes(context -> {
|
||||
ServerCommandSource source = context.getSource();
|
||||
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) {
|
||||
source.sendError(Text.literal("Backrooms dimension not found"));
|
||||
return 0;
|
||||
}
|
||||
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.BLACKOUT;
|
||||
BackroomsLightManager.eventTimer = 3600;
|
||||
BackroomsLightManager.cooldownTimer = 3600;
|
||||
BackroomsLightManager.forceBlackout(backrooms);
|
||||
source.sendMessage(Text.literal("§4Backrooms lights set to blackout"));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
Registry.register(
|
||||
Registries.PAINTING_VARIANT,
|
||||
@@ -1088,6 +1174,7 @@ public class Szar implements ModInitializer {
|
||||
new Identifier(MOD_ID, "overworld_portal"))
|
||||
);
|
||||
BackroomsBarrelManager.register();
|
||||
BackroomsLightManager.register();
|
||||
}
|
||||
// Blocks
|
||||
public static final TrackerBlock TRACKER_BLOCK = Registry.register(
|
||||
@@ -1715,6 +1802,31 @@ public class Szar implements ModInitializer {
|
||||
new Identifier(MOD_ID, "can_of_beans"),
|
||||
new CanOfBeansItem(new Item.Settings())
|
||||
);
|
||||
public static final Item ALMOND_WATER = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(MOD_ID, "almond_water"),
|
||||
new AlmondWaterItem(new Item.Settings())
|
||||
);
|
||||
public static final BackroomsLightBlock BACKROOMS_LIGHT = Registry.register(
|
||||
Registries.BLOCK, new Identifier(MOD_ID, "backrooms_light"),
|
||||
new BackroomsLightBlock(FabricBlockSettings.create()
|
||||
.nonOpaque()
|
||||
.luminance(state -> BackroomsLightBlock.getLightLevel(state))
|
||||
.strength(0.3f))
|
||||
);
|
||||
|
||||
public static final BlockItem BACKROOMS_LIGHT_ITEM = Registry.register(
|
||||
Registries.ITEM, new Identifier(MOD_ID, "backrooms_light"),
|
||||
new BlockItem(BACKROOMS_LIGHT, new FabricItemSettings())
|
||||
);
|
||||
|
||||
public static final BlockEntityType<BackroomsLightBlockEntity> BACKROOMS_LIGHT_ENTITY =
|
||||
Registry.register(
|
||||
Registries.BLOCK_ENTITY_TYPE,
|
||||
new Identifier(MOD_ID, "backrooms_light"),
|
||||
FabricBlockEntityTypeBuilder.create(BackroomsLightBlockEntity::new,
|
||||
BACKROOMS_LIGHT).build()
|
||||
);
|
||||
public static final SoundEvent BAITER =
|
||||
SoundEvent.of(new Identifier(MOD_ID, "baiter"));
|
||||
public static final Item BAITER_DISC = Registry.register(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"variants": {
|
||||
"light_state=on": { "model": "szar:block/backrooms_light_on" },
|
||||
"light_state=off": { "model": "szar:block/backrooms_light_off" },
|
||||
"light_state=flickering_on": { "model": "szar:block/backrooms_light_on" },
|
||||
"light_state=flickering_off": { "model": "szar:block/backrooms_light_off" }
|
||||
}
|
||||
}
|
||||
@@ -147,5 +147,7 @@
|
||||
"block.szar.wall_bottom": "Wall Bottom",
|
||||
|
||||
"item.szar.bean": "Bean",
|
||||
"item.szar.can_of_beans": "Can of Beans"
|
||||
"item.szar.can_of_beans": "Can of Beans",
|
||||
"item.szar.almond_water": "Almond Water",
|
||||
"block.szar.backrooms_light": "Light"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "szar:block/black"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "szar:block/white"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"format_version": "1.21.11",
|
||||
"credit": "Made with Blockbench",
|
||||
"parent": "block/block",
|
||||
"texture_size": [64, 64],
|
||||
"textures": {
|
||||
"2": "szar:block/wallpaper_bottom_block_texture",
|
||||
@@ -32,12 +33,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [80.5, 45, 0],
|
||||
"scale": [0.3, 0.3, 0.3]
|
||||
}
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"name": "block",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "szar:item/almond_water"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"parent": "szar:block/backrooms_light_on"
|
||||
}
|
||||
BIN
src/main/resources/assets/szar/textures/block/black.png
Normal file
BIN
src/main/resources/assets/szar/textures/block/black.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 558 B |
BIN
src/main/resources/assets/szar/textures/item/almond_water.png
Normal file
BIN
src/main/resources/assets/szar/textures/item/almond_water.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 808 B |
@@ -4,7 +4,7 @@
|
||||
"coordinate_scale": 1.0,
|
||||
"has_skylight": false,
|
||||
"has_ceiling": true,
|
||||
"ambient_light": 0.5,
|
||||
"ambient_light": 0.0,
|
||||
"fixed_time": 18000,
|
||||
"monster_spawn_light_level": 0,
|
||||
"monster_spawn_block_light_limit": 0,
|
||||
@@ -16,5 +16,5 @@
|
||||
"min_y": 0,
|
||||
"height": 64,
|
||||
"infiniburn": "#minecraft:infiniburn_overworld",
|
||||
"effects": "minecraft:the_end"
|
||||
"effects": "minecraft:overworld"
|
||||
}
|
||||
Reference in New Issue
Block a user