backrooms entity, lights fix and beer
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.19
|
||||
mod_version=26.3.20
|
||||
maven_group=dev.tggamesyt
|
||||
archives_base_name=szar
|
||||
# Dependencies
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package dev.tggamesyt.szar.client;
|
||||
|
||||
import dev.tggamesyt.szar.BackroomsLightBlock;
|
||||
import dev.tggamesyt.szar.BackroomsLightBlockEntity;
|
||||
import dev.tggamesyt.szar.BackroomsLightManager;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
@@ -14,7 +17,6 @@ import org.joml.Matrix4f;
|
||||
|
||||
public class BackroomsLightBlockEntityRenderer implements BlockEntityRenderer<BackroomsLightBlockEntity> {
|
||||
|
||||
// Your light texture
|
||||
private static final Identifier TEXTURE =
|
||||
new Identifier("szar", "textures/block/white.png");
|
||||
|
||||
@@ -25,23 +27,38 @@ public class BackroomsLightBlockEntityRenderer implements BlockEntityRenderer<Ba
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers,
|
||||
int light, int overlay) {
|
||||
|
||||
float brightness = entity.brightness;
|
||||
if (brightness <= 0.0f) return; // fully dark, don't render
|
||||
BlockState state = entity.getCachedState();
|
||||
BackroomsLightBlock.LightState ls = state.get(BackroomsLightBlock.LIGHT_STATE);
|
||||
|
||||
float brightness;
|
||||
if (ls == BackroomsLightBlock.LightState.OFF) {
|
||||
brightness = 0.0f;
|
||||
} else if (ls == BackroomsLightBlock.LightState.ON) {
|
||||
// Check if there's a global flicker event — if so flicker this light too
|
||||
if (BackroomsLightManager.currentEvent == BackroomsLightManager.GlobalEvent.FLICKER) {
|
||||
brightness = computeFlicker(entity, tickDelta);
|
||||
} else {
|
||||
brightness = 1.0f;
|
||||
}
|
||||
} else {
|
||||
// FLICKERING — always flicker regardless of event
|
||||
brightness = computeFlicker(entity, tickDelta);
|
||||
}
|
||||
|
||||
BlockPos pos = entity.getPos();
|
||||
int lightLevel = WorldRenderer.getLightmapCoordinates(entity.getWorld(), pos);
|
||||
int lightLevel = brightness > 0
|
||||
? WorldRenderer.getLightmapCoordinates(entity.getWorld(), pos)
|
||||
: 0;
|
||||
|
||||
VertexConsumer consumer = vertexConsumers.getBuffer(
|
||||
RenderLayer.getEntityCutoutNoCull(TEXTURE));
|
||||
|
||||
matrices.push();
|
||||
// Center on block, render on bottom face (light faces downward into room)
|
||||
matrices.translate(0.5, 0.001, 0.5);
|
||||
matrices.translate(0.5, -0.001, 0.5);
|
||||
matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_X.rotationDegrees(90));
|
||||
|
||||
Matrix4f matrix = matrices.peek().getPositionMatrix();
|
||||
|
||||
// Apply brightness as color multiplier
|
||||
int r = (int)(255 * brightness);
|
||||
int g = (int)(255 * brightness);
|
||||
int b = (int)(255 * brightness);
|
||||
@@ -62,4 +79,22 @@ public class BackroomsLightBlockEntityRenderer implements BlockEntityRenderer<Ba
|
||||
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
private float computeFlicker(BackroomsLightBlockEntity entity, float tickDelta) {
|
||||
// Sample a pseudo-random noise function using global timer + per-block offset
|
||||
// This produces independent flicker per block with no server communication
|
||||
int t = BackroomsLightManager.globalFlickerTimer + entity.flickerOffset;
|
||||
|
||||
// Multi-frequency noise for natural-looking flicker
|
||||
double n = Math.sin(t * 0.3) * 0.3
|
||||
+ Math.sin(t * 0.7 + entity.flickerOffset) * 0.2
|
||||
+ Math.sin(t * 1.3 + entity.flickerOffset * 0.5) * 0.1;
|
||||
|
||||
// Occasionally snap to near-zero for a sharp flicker
|
||||
boolean snap = ((t + entity.flickerOffset) % 23) < 2;
|
||||
if (snap) return 0.05f + (float)(Math.random() * 0.1);
|
||||
|
||||
float base = 0.75f + (float)(n * 0.8);
|
||||
return Math.max(0.05f, Math.min(1.0f, base));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package dev.tggamesyt.szar.client;
|
||||
|
||||
import dev.tggamesyt.szar.SmilerEffectManager;
|
||||
import dev.tggamesyt.szar.SmilerType;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.sound.PositionedSoundInstance;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class SmilerEffectRenderer {
|
||||
|
||||
private static float flashbangAlpha = 0f;
|
||||
private static float jumpscareAlpha = 0f;
|
||||
private static SmilerType jumpscareType = null;
|
||||
private static int jumpscareTimer = 0;
|
||||
|
||||
private static final Identifier EYES_FACE =
|
||||
new Identifier("szar", "textures/entity/eyes.png");
|
||||
private static final Identifier SCARY_FACE =
|
||||
new Identifier("szar", "textures/entity/scary.png");
|
||||
private static final Identifier REAL_FACE =
|
||||
new Identifier("szar", "textures/entity/real.png");
|
||||
|
||||
public static void register() {
|
||||
|
||||
// Flashbang packet (kept as-is, but mainly used by EYES now)
|
||||
ClientPlayNetworking.registerGlobalReceiver(
|
||||
SmilerEffectManager.FLASHBANG_PACKET, (client, handler, buf, responseSender) -> {
|
||||
client.execute(() -> flashbangAlpha = 1.0f);
|
||||
});
|
||||
|
||||
// Jumpscare packet
|
||||
ClientPlayNetworking.registerGlobalReceiver(
|
||||
SmilerEffectManager.JUMPSCARE_PACKET, (client, handler, buf, responseSender) -> {
|
||||
String typeName = buf.readString();
|
||||
SmilerType type = SmilerType.valueOf(typeName);
|
||||
|
||||
client.execute(() -> {
|
||||
jumpscareType = type;
|
||||
jumpscareTimer = 40;
|
||||
|
||||
// 🔊 Play correct sound per type
|
||||
Identifier soundId = switch (type) {
|
||||
case EYES -> new Identifier("szar", "flashbang");
|
||||
case SCARY -> new Identifier("szar", "scary");
|
||||
case REAL -> new Identifier("szar", "real");
|
||||
};
|
||||
|
||||
client.getSoundManager().play(
|
||||
PositionedSoundInstance.master(
|
||||
SoundEvent.of(soundId), 1.0f));
|
||||
|
||||
// 💥 Special handling for EYES
|
||||
if (type == SmilerType.EYES) {
|
||||
flashbangAlpha = 1.0f;
|
||||
jumpscareAlpha = 0f; // no face rendering
|
||||
} else {
|
||||
jumpscareAlpha = 1.0f;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// HUD renderer
|
||||
HudRenderCallback.EVENT.register(SmilerEffectRenderer::renderHud);
|
||||
}
|
||||
|
||||
private static void renderHud(DrawContext context, float tickDelta) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.player == null) return;
|
||||
|
||||
int screenW = client.getWindow().getScaledWidth();
|
||||
int screenH = client.getWindow().getScaledHeight();
|
||||
|
||||
// 💥 Flashbang
|
||||
if (flashbangAlpha > 0) {
|
||||
int alpha = (int)(flashbangAlpha * 255);
|
||||
context.fill(0, 0, screenW, screenH,
|
||||
(alpha << 24) | 0x00FFFFFF);
|
||||
flashbangAlpha = Math.max(0, flashbangAlpha - 0.02f);
|
||||
}
|
||||
|
||||
// 👁️ Jumpscare (ONLY if not EYES)
|
||||
if (jumpscareAlpha > 0 && jumpscareType != null && jumpscareType != SmilerType.EYES) {
|
||||
jumpscareTimer--;
|
||||
|
||||
Identifier faceTexture = switch (jumpscareType) {
|
||||
case SCARY -> SCARY_FACE;
|
||||
case REAL -> REAL_FACE;
|
||||
default -> SCARY_FACE;
|
||||
};
|
||||
|
||||
int alpha = (int)(jumpscareAlpha * 255);
|
||||
|
||||
context.drawTexture(faceTexture,
|
||||
0, 0, screenW, screenH,
|
||||
0, 0, 768, 768,
|
||||
1536, 1536);
|
||||
|
||||
context.fill(0, 0, screenW, screenH,
|
||||
(alpha << 24) & 0xFF000000);
|
||||
|
||||
if (jumpscareTimer <= 0) {
|
||||
jumpscareAlpha = Math.max(0, jumpscareAlpha - 0.05f);
|
||||
if (jumpscareAlpha <= 0) jumpscareType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package dev.tggamesyt.szar.client;
|
||||
|
||||
import dev.tggamesyt.szar.SmilerEntity;
|
||||
import dev.tggamesyt.szar.SmilerType;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
public class SmilerRenderer extends EntityRenderer<SmilerEntity> {
|
||||
|
||||
private static final Identifier EYES_TEX =
|
||||
new Identifier("szar", "textures/entity/eyes.png");
|
||||
private static final Identifier SCARY_TEX =
|
||||
new Identifier("szar", "textures/entity/scary.png");
|
||||
private static final Identifier REAL_TEX =
|
||||
new Identifier("szar", "textures/entity/real.png");
|
||||
|
||||
public SmilerRenderer(EntityRendererFactory.Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTexture(SmilerEntity entity) {
|
||||
return switch (entity.smilerType) {
|
||||
case EYES -> EYES_TEX;
|
||||
case SCARY -> SCARY_TEX;
|
||||
case REAL -> REAL_TEX;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SmilerEntity entity, float yaw, float tickDelta,
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers,
|
||||
int light) {
|
||||
matrices.push();
|
||||
|
||||
// Billboard — face camera
|
||||
matrices.multiply(this.dispatcher.getRotation());
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f));
|
||||
|
||||
// Scale to roughly 1.5 blocks tall
|
||||
matrices.scale(1.5f, 1.5f, 1.5f);
|
||||
|
||||
Matrix4f matrix = matrices.peek().getPositionMatrix();
|
||||
|
||||
Identifier texture = getTexture(entity);
|
||||
VertexConsumer consumer = vertexConsumers.getBuffer(
|
||||
RenderLayer.getEntityTranslucent(texture));
|
||||
|
||||
float s = 0.5f;
|
||||
|
||||
// Top-left quarter UVs
|
||||
float u0 = 0.0f;
|
||||
float v0 = 0.0f;
|
||||
float u1 = 0.5f;
|
||||
float v1 = 0.5f;
|
||||
|
||||
int fullBright = 0xF000F0;
|
||||
|
||||
consumer.vertex(matrix, -s, -s, 0).color(255, 255, 255, 255)
|
||||
.texture(u0, v1).overlay(OverlayTexture.DEFAULT_UV).light(fullBright)
|
||||
.normal(0, 1, 0).next();
|
||||
|
||||
consumer.vertex(matrix, s, -s, 0).color(255, 255, 255, 255)
|
||||
.texture(u1, v1).overlay(OverlayTexture.DEFAULT_UV).light(fullBright)
|
||||
.normal(0, 1, 0).next();
|
||||
|
||||
consumer.vertex(matrix, s, s, 0).color(255, 255, 255, 255)
|
||||
.texture(u1, v0).overlay(OverlayTexture.DEFAULT_UV).light(fullBright)
|
||||
.normal(0, 1, 0).next();
|
||||
|
||||
consumer.vertex(matrix, -s, s, 0).color(255, 255, 255, 255)
|
||||
.texture(u0, v0).overlay(OverlayTexture.DEFAULT_UV).light(fullBright)
|
||||
.normal(0, 1, 0).next();
|
||||
|
||||
matrices.pop();
|
||||
super.render(entity, yaw, tickDelta, matrices, vertexConsumers, light);
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,8 @@ public class SzarClient implements ClientModInitializer {
|
||||
);
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
SmilerEffectRenderer.register();
|
||||
EntityRendererRegistry.register(Szar.SMILER_ENTITY_TYPE, SmilerRenderer::new);
|
||||
ClientPlayNetworking.registerGlobalReceiver(OPEN_DETONATOR_SCREEN, (client, handler, buf, responseSender) -> {
|
||||
client.execute(() -> client.setScreen(new CoordInputScreen()));
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.block.Blocks;
|
||||
import net.minecraft.registry.RegistryCodecs;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.ChunkRegion;
|
||||
import net.minecraft.world.HeightLimitView;
|
||||
@@ -19,6 +20,7 @@ import net.minecraft.world.biome.BiomeKeys;
|
||||
import net.minecraft.world.biome.source.BiomeAccess;
|
||||
import net.minecraft.world.biome.source.FixedBiomeSource;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
import net.minecraft.world.gen.GenerationStep;
|
||||
import net.minecraft.world.gen.StructureAccessor;
|
||||
import net.minecraft.world.gen.chunk.Blender;
|
||||
@@ -93,12 +95,10 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
|
||||
long lightRoll = hash(worldX * 53 + 7, worldZ * 47 + 13);
|
||||
int roll = (int)(Math.abs(lightRoll) % 100);
|
||||
if (roll < 98) {
|
||||
// 98% ON (flickering determined by block entity in generateFeatures)
|
||||
ceilingBlock = Szar.BACKROOMS_LIGHT.getDefaultState()
|
||||
.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.ON);
|
||||
} else {
|
||||
// 2% missing — ceiling block, no light
|
||||
ceilingBlock = Szar.CEILING.getDefaultState();
|
||||
}
|
||||
} else {
|
||||
@@ -283,19 +283,23 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize light block entities
|
||||
// Initialize light block entities — set mutable explicitly to Y=9
|
||||
mutable.set(chunkX + lx, 9, chunkZ + lz);
|
||||
BlockState lightState = world.getBlockState(mutable);
|
||||
if (lightState.getBlock() instanceof BackroomsLightBlock) {
|
||||
// Always re-set to force block entity creation — no null check needed
|
||||
world.setBlockState(mutable, lightState, Block.NOTIFY_ALL);
|
||||
BlockPos immutable = mutable.toImmutable();
|
||||
if (world.getBlockEntity(immutable) instanceof BackroomsLightBlockEntity light) {
|
||||
long typeRoll = hash(chunkX + lx, chunkZ + lz);
|
||||
light.isFlickering = (Math.abs(typeRoll) % 10) >= 8;
|
||||
light.flickerOffset = (int)(Math.abs(hash(chunkX + lx, chunkZ + lz)) % 100);
|
||||
light.flickerTimer = light.flickerOffset;
|
||||
light.flickerOffset = (int)(Math.abs(hash(chunkX + lx, chunkZ + lz)) % 1000);
|
||||
light.brightness = 1.0f;
|
||||
if (light.isFlickering) {
|
||||
world.setBlockState(immutable,
|
||||
world.getBlockState(immutable).with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.FLICKERING),
|
||||
Block.NOTIFY_ALL);
|
||||
}
|
||||
light.markDirty();
|
||||
}
|
||||
}
|
||||
@@ -324,5 +328,11 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At the end of generateFeatures, after all block entity init:
|
||||
if (world instanceof ServerWorld sw) {
|
||||
WorldChunk wc = sw.getChunk(chunk.getPos().x, chunk.getPos().z);
|
||||
BackroomsLightManager.applyCurrentEventToChunk(sw, wc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,18 @@ 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;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class BackroomsLightBlock extends BlockWithEntity {
|
||||
|
||||
public enum LightState implements StringIdentifiable {
|
||||
ON, OFF;
|
||||
ON, OFF, FLICKERING;
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
public String asString() { return name().toLowerCase(); }
|
||||
}
|
||||
|
||||
public static final EnumProperty<LightState> LIGHT_STATE =
|
||||
@@ -52,13 +51,8 @@ public class BackroomsLightBlock extends BlockWithEntity {
|
||||
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)
|
||||
? (w, pos, s, be) ->
|
||||
BackroomsLightBlockEntity.tick(w, pos, s, (BackroomsLightBlockEntity) be)
|
||||
: null;
|
||||
}
|
||||
|
||||
// Light level based on state
|
||||
public static int getLightLevel(BlockState state) {
|
||||
return state.get(LIGHT_STATE) == LightState.ON ? 15 : 0;
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,12 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BackroomsLightBlockEntity extends BlockEntity {
|
||||
public float brightness = 1.0f; // 0.0 = black, 1.0 = full bright
|
||||
// Random offset so each light flickers at different times
|
||||
public boolean isFlickering = false;
|
||||
// Used by renderer — offset into the global flicker timer
|
||||
public int flickerOffset = 0;
|
||||
// How many ticks until next state toggle during flicker
|
||||
public int flickerTimer = 0;
|
||||
// Visual brightness — only changed during blackout, not during flicker
|
||||
public float brightness = 1.0f;
|
||||
private boolean initialized = false;
|
||||
public boolean isFlickering = false; // true for lights generated as flickering type
|
||||
|
||||
public BackroomsLightBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(Szar.BACKROOMS_LIGHT_ENTITY, pos, state);
|
||||
@@ -25,36 +24,32 @@ public class BackroomsLightBlockEntity extends BlockEntity {
|
||||
public static void tick(World world, BlockPos pos, BlockState state,
|
||||
BackroomsLightBlockEntity entity) {
|
||||
if (!entity.initialized) {
|
||||
entity.flickerOffset = world.random.nextInt(100);
|
||||
entity.flickerOffset = world.random.nextInt(1000);
|
||||
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);
|
||||
nbt.putFloat("Brightness", brightness);
|
||||
nbt.putBoolean("Initialized", initialized);
|
||||
nbt.putBoolean("IsFlickering", isFlickering);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound nbt) {
|
||||
super.readNbt(nbt);
|
||||
flickerOffset = nbt.getInt("FlickerOffset");
|
||||
flickerTimer = nbt.getInt("FlickerTimer");
|
||||
initialized = nbt.getBoolean("Initialized");
|
||||
brightness = nbt.getFloat("Brightness");
|
||||
initialized = nbt.getBoolean("Initialized");
|
||||
isFlickering = nbt.getBoolean("IsFlickering");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toInitialChunkDataNbt() {
|
||||
return createNbt();
|
||||
}
|
||||
public NbtCompound toInitialChunkDataNbt() { return createNbt(); }
|
||||
|
||||
@Override
|
||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.block.Block;
|
||||
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;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BackroomsLightManager {
|
||||
|
||||
@@ -19,6 +18,7 @@ public class BackroomsLightManager {
|
||||
public static GlobalEvent currentEvent = GlobalEvent.NONE;
|
||||
public static int eventTimer = 0;
|
||||
public static int cooldownTimer = 3600;
|
||||
public static int globalFlickerTimer = 0;
|
||||
|
||||
private static final int FLICKER_DURATION_MIN = 60;
|
||||
private static final int FLICKER_DURATION_MAX = 160;
|
||||
@@ -34,26 +34,46 @@ public class BackroomsLightManager {
|
||||
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) return;
|
||||
|
||||
globalFlickerTimer++;
|
||||
|
||||
// Every 20 ticks, fix any lights in wrong state for current event
|
||||
if (globalFlickerTimer % 20 == 0) {
|
||||
if (currentEvent == GlobalEvent.BLACKOUT) {
|
||||
forEachLightEntity(backrooms, (entity, state, pos) -> {
|
||||
if (state.get(BackroomsLightBlock.LIGHT_STATE)
|
||||
!= BackroomsLightBlock.LightState.OFF) {
|
||||
backrooms.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.OFF), Block.NOTIFY_ALL);
|
||||
}
|
||||
});
|
||||
} else if (currentEvent == GlobalEvent.NONE) {
|
||||
forEachLightEntity(backrooms, (entity, state, pos) -> {
|
||||
if (state.get(BackroomsLightBlock.LIGHT_STATE) == BackroomsLightBlock.LightState.OFF) {
|
||||
BackroomsLightBlock.LightState restore = entity.isFlickering
|
||||
? BackroomsLightBlock.LightState.FLICKERING
|
||||
: BackroomsLightBlock.LightState.ON;
|
||||
backrooms.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
restore), Block.NOTIFY_ALL);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (currentEvent != GlobalEvent.NONE) {
|
||||
eventTimer--;
|
||||
if (eventTimer <= 0) {
|
||||
endEvent(backrooms);
|
||||
}
|
||||
if (eventTimer <= 0) endEvent(backrooms);
|
||||
} else {
|
||||
cooldownTimer--;
|
||||
if (cooldownTimer <= 0) {
|
||||
int roll = backrooms.random.nextInt(100);
|
||||
if (roll < 30) {
|
||||
startBlackout(backrooms);
|
||||
} else if (roll < 63) {
|
||||
startFlicker(backrooms);
|
||||
} else {
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
}
|
||||
if (roll < 30) startBlackout(backrooms);
|
||||
else if (roll < 63) startFlicker(backrooms);
|
||||
else cooldownTimer = EVENT_COOLDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void startFlicker(ServerWorld world) {
|
||||
currentEvent = GlobalEvent.FLICKER;
|
||||
eventTimer = FLICKER_DURATION_MIN + world.random.nextInt(
|
||||
@@ -65,65 +85,66 @@ public class BackroomsLightManager {
|
||||
currentEvent = GlobalEvent.BLACKOUT;
|
||||
eventTimer = BLACKOUT_MIN + world.random.nextInt(BLACKOUT_MAX - BLACKOUT_MIN);
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
// Set all lights to brightness 0
|
||||
forEachLightEntity(world, entity -> {
|
||||
entity.brightness = 0.0f;
|
||||
entity.markDirty();
|
||||
forEachLightEntity(world, (entity, state, pos) -> {
|
||||
if (state.get(BackroomsLightBlock.LIGHT_STATE) != BackroomsLightBlock.LightState.OFF) {
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.OFF), Block.NOTIFY_ALL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void endEvent(ServerWorld world) {
|
||||
// Restore all lights to full brightness
|
||||
forEachLightEntity(world, entity -> {
|
||||
entity.brightness = 1.0f;
|
||||
entity.markDirty();
|
||||
});
|
||||
currentEvent = GlobalEvent.NONE;
|
||||
eventTimer = 0;
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
}
|
||||
|
||||
// Called per-light from BackroomsLightBlockEntity.tick
|
||||
public static void tickLight(World world, BlockPos pos, BlockState state,
|
||||
BackroomsLightBlockEntity entity) {
|
||||
if (currentEvent == GlobalEvent.BLACKOUT) return;
|
||||
|
||||
forEachLightEntity(world, (entity, state, pos) -> {
|
||||
BackroomsLightBlock.LightState ls = state.get(BackroomsLightBlock.LIGHT_STATE);
|
||||
if (ls == BackroomsLightBlock.LightState.OFF) return;
|
||||
|
||||
boolean inFlickerEvent = currentEvent == GlobalEvent.FLICKER;
|
||||
|
||||
// Always-flickering lights tick regardless of event
|
||||
// During flicker event, all ON lights also flicker
|
||||
if (!entity.isFlickering && !inFlickerEvent) {
|
||||
// Normal ON light, not in event — ensure full brightness
|
||||
if (entity.brightness != 1.0f) {
|
||||
entity.brightness = 1.0f;
|
||||
entity.markDirty();
|
||||
// Restore any light that was turned off by an event
|
||||
if (ls == BackroomsLightBlock.LightState.OFF) {
|
||||
BackroomsLightBlock.LightState restore = entity.isFlickering
|
||||
? BackroomsLightBlock.LightState.FLICKERING
|
||||
: BackroomsLightBlock.LightState.ON;
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
restore), Block.NOTIFY_ALL);
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
entity.flickerTimer--;
|
||||
if (entity.flickerTimer > 0) return;
|
||||
// Called from generateFeatures when a new chunk loads — apply current event state
|
||||
public static void applyCurrentEventToChunk(ServerWorld world, WorldChunk chunk) {
|
||||
if (currentEvent == GlobalEvent.NONE) return;
|
||||
|
||||
// Random new brightness and timer
|
||||
float newBrightness;
|
||||
if (world.random.nextFloat() < 0.3f) {
|
||||
// 30% chance of a dim flicker
|
||||
newBrightness = 0.1f + world.random.nextFloat() * 0.4f;
|
||||
} else {
|
||||
// 70% chance of full or near-full
|
||||
newBrightness = 0.7f + world.random.nextFloat() * 0.3f;
|
||||
int cx = chunk.getPos().getStartX();
|
||||
int cz = chunk.getPos().getStartZ();
|
||||
BlockPos.Mutable mutable = new BlockPos.Mutable();
|
||||
|
||||
for (int lx = 0; lx < 16; lx++) {
|
||||
for (int lz = 0; lz < 16; lz++) {
|
||||
mutable.set(cx + lx, 9, cz + lz);
|
||||
BlockPos immutable = mutable.toImmutable();
|
||||
BlockState state = world.getBlockState(immutable);
|
||||
if (!(state.getBlock() instanceof BackroomsLightBlock)) continue;
|
||||
if (!(world.getBlockEntity(immutable) instanceof BackroomsLightBlockEntity entity)) continue;
|
||||
|
||||
if (currentEvent == GlobalEvent.BLACKOUT) {
|
||||
if (state.get(BackroomsLightBlock.LIGHT_STATE)
|
||||
!= BackroomsLightBlock.LightState.OFF) {
|
||||
world.setBlockState(immutable, state.with(
|
||||
BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.OFF), Block.NOTIFY_ALL);
|
||||
}
|
||||
}
|
||||
// FLICKER is handled per-tick so no chunk-load action needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entity.brightness = newBrightness;
|
||||
entity.flickerTimer = 2 + world.random.nextInt(8 + (entity.flickerOffset % 5));
|
||||
entity.markDirty();
|
||||
@FunctionalInterface
|
||||
interface LightConsumer {
|
||||
void accept(BackroomsLightBlockEntity entity, BlockState state, BlockPos pos);
|
||||
}
|
||||
|
||||
private static void forEachLightEntity(ServerWorld world,
|
||||
Consumer<BackroomsLightBlockEntity> consumer) {
|
||||
private static void forEachLightEntity(ServerWorld world, LightConsumer consumer) {
|
||||
for (WorldChunk chunk : getLoadedChunks(world)) {
|
||||
int cx = chunk.getPos().getStartX();
|
||||
int cz = chunk.getPos().getStartZ();
|
||||
@@ -131,9 +152,12 @@ public class BackroomsLightManager {
|
||||
for (int lx = 0; lx < 16; lx++) {
|
||||
for (int lz = 0; lz < 16; lz++) {
|
||||
mutable.set(cx + lx, 9, cz + lz);
|
||||
if (world.getBlockEntity(mutable.toImmutable())
|
||||
BlockPos immutable = mutable.toImmutable();
|
||||
BlockState state = world.getBlockState(immutable);
|
||||
if (state.getBlock() instanceof BackroomsLightBlock
|
||||
&& world.getBlockEntity(immutable)
|
||||
instanceof BackroomsLightBlockEntity entity) {
|
||||
consumer.accept(entity);
|
||||
consumer.accept(entity, state, immutable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,21 +183,23 @@ public class BackroomsLightManager {
|
||||
}
|
||||
|
||||
public static void forceRestoreAllLights(ServerWorld world) {
|
||||
forEachLightEntity(world, entity -> {
|
||||
entity.brightness = 1.0f;
|
||||
entity.markDirty();
|
||||
});
|
||||
currentEvent = GlobalEvent.NONE;
|
||||
eventTimer = 0;
|
||||
cooldownTimer = EVENT_COOLDOWN;
|
||||
forEachLightEntity(world, (entity, state, pos) -> {
|
||||
BackroomsLightBlock.LightState restore = entity.isFlickering
|
||||
? BackroomsLightBlock.LightState.FLICKERING
|
||||
: BackroomsLightBlock.LightState.ON;
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
restore), Block.NOTIFY_ALL);
|
||||
});
|
||||
}
|
||||
|
||||
public static void forceBlackout(ServerWorld world) {
|
||||
forEachLightEntity(world, entity -> {
|
||||
entity.brightness = 0.0f;
|
||||
entity.markDirty();
|
||||
});
|
||||
currentEvent = GlobalEvent.BLACKOUT;
|
||||
eventTimer = 3600;
|
||||
forEachLightEntity(world, (entity, state, pos) ->
|
||||
world.setBlockState(pos, state.with(BackroomsLightBlock.LIGHT_STATE,
|
||||
BackroomsLightBlock.LightState.OFF), Block.NOTIFY_ALL));
|
||||
}
|
||||
}
|
||||
94
src/main/java/dev/tggamesyt/szar/BeerItem.java
Normal file
94
src/main/java/dev/tggamesyt/szar/BeerItem.java
Normal file
@@ -0,0 +1,94 @@
|
||||
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.StatusEffects;
|
||||
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;
|
||||
|
||||
public class BeerItem extends Item {
|
||||
|
||||
public BeerItem(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) {
|
||||
// 🍺 Give nausea (10 seconds = 200 ticks)
|
||||
user.addStatusEffect(new StatusEffectInstance(StatusEffects.NAUSEA, 60 * 20, 0));
|
||||
user.addStatusEffect(new StatusEffectInstance(Szar.DRUNK_EFFECT, 60 * 20, 0));
|
||||
|
||||
// Optional: tiny hunger boost like drinks
|
||||
if (player != null) {
|
||||
player.getHungerManager().add(2, 0.2F);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
43
src/main/java/dev/tggamesyt/szar/DrunkEffect.java
Normal file
43
src/main/java/dev/tggamesyt/szar/DrunkEffect.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.effect.StatusEffect;
|
||||
import net.minecraft.entity.effect.StatusEffectCategory;
|
||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.hit.EntityHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.world.RaycastContext;
|
||||
|
||||
public class DrunkEffect extends StatusEffect {
|
||||
|
||||
public DrunkEffect() {
|
||||
super(StatusEffectCategory.HARMFUL, 0xFF9900);
|
||||
}
|
||||
|
||||
static void tick(MinecraftServer server) {
|
||||
for (var world : server.getWorlds()) {
|
||||
// Every 20 ticks = 1 second
|
||||
if (world.getTime() % 20 != 0) continue;
|
||||
|
||||
for (ServerPlayerEntity player : world.getPlayers()) {
|
||||
if (!player.hasStatusEffect(Szar.DRUNK_EFFECT)) continue;
|
||||
|
||||
// 1 in 5 chance
|
||||
if (world.random.nextInt(5) != 0) continue;
|
||||
|
||||
// Raycast to find what the player is looking at
|
||||
HitResult hit = player.raycast(4.5, 0, false);
|
||||
if (hit.getType() != HitResult.Type.ENTITY) continue;
|
||||
if (!(hit instanceof EntityHitResult entityHit)) continue;
|
||||
if (!(entityHit.getEntity() instanceof LivingEntity target)) continue;
|
||||
|
||||
// Hit the entity as if the player attacked it
|
||||
player.attack(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/main/java/dev/tggamesyt/szar/SmilerEffectManager.java
Normal file
24
src/main/java/dev/tggamesyt/szar/SmilerEffectManager.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class SmilerEffectManager {
|
||||
|
||||
public static final Identifier FLASHBANG_PACKET = new Identifier("szar", "flashbang");
|
||||
public static final Identifier JUMPSCARE_PACKET = new Identifier("szar", "jumpscare");
|
||||
|
||||
public static void triggerFlashbang(ServerPlayerEntity player) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
ServerPlayNetworking.send(player, FLASHBANG_PACKET, buf);
|
||||
}
|
||||
|
||||
public static void triggerJumpscare(ServerPlayerEntity player, SmilerType type) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeString(type.name());
|
||||
ServerPlayNetworking.send(player, JUMPSCARE_PACKET, buf);
|
||||
}
|
||||
}
|
||||
225
src/main/java/dev/tggamesyt/szar/SmilerEntity.java
Normal file
225
src/main/java/dev/tggamesyt/szar/SmilerEntity.java
Normal file
@@ -0,0 +1,225 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.minecraft.entity.*;
|
||||
import net.minecraft.entity.ai.goal.*;
|
||||
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
||||
import net.minecraft.entity.attribute.EntityAttributes;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.entity.mob.PathAwareEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.LocalDifficulty;
|
||||
import net.minecraft.world.ServerWorldAccess;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class SmilerEntity extends PathAwareEntity {
|
||||
|
||||
public SmilerType smilerType = SmilerType.EYES;
|
||||
|
||||
// Targeting
|
||||
@Nullable private UUID targetPlayerUUID = null;
|
||||
@Nullable private PlayerEntity cachedTarget = null;
|
||||
|
||||
// State tracking
|
||||
private int stateTimer = 0;
|
||||
private boolean hasActed = false; // has this entity done its main action?
|
||||
|
||||
// SCARY specific
|
||||
private boolean scaryTeleported = false;
|
||||
private boolean playerWasLooking = false;
|
||||
|
||||
public SmilerEntity(EntityType<? extends PathAwareEntity> type, World world) {
|
||||
super(type, world);
|
||||
this.setInvisible(false);
|
||||
this.setNoGravity(true);
|
||||
}
|
||||
|
||||
public static DefaultAttributeContainer.Builder createAttributes() {
|
||||
return MobEntity.createMobAttributes()
|
||||
.add(EntityAttributes.GENERIC_MAX_HEALTH, 1.0)
|
||||
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.15)
|
||||
.add(EntityAttributes.GENERIC_FOLLOW_RANGE, 64.0)
|
||||
.add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initGoals() {
|
||||
// No standard goals — all behavior handled in tick
|
||||
}
|
||||
|
||||
public void setTargetPlayer(PlayerEntity player) {
|
||||
if (player.isCreative() || player.isSpectator()) return;
|
||||
|
||||
this.targetPlayerUUID = player.getUuid();
|
||||
this.cachedTarget = player;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PlayerEntity getTargetPlayer() {
|
||||
if (cachedTarget != null && cachedTarget.isAlive()) return cachedTarget;
|
||||
if (targetPlayerUUID != null && getWorld() instanceof ServerWorld sw) {
|
||||
cachedTarget = sw.getPlayerByUuid(targetPlayerUUID);
|
||||
return cachedTarget;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (getWorld().isClient) return;
|
||||
|
||||
PlayerEntity player = getTargetPlayer();
|
||||
|
||||
if (player == null || !player.isAlive() || player.isCreative() || player.isSpectator()) {
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
|
||||
// Despawn if blackout ended
|
||||
if (BackroomsLightManager.currentEvent != BackroomsLightManager.GlobalEvent.BLACKOUT) {
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
|
||||
stateTimer++;
|
||||
|
||||
switch (smilerType) {
|
||||
case EYES -> tickEyes(player);
|
||||
case SCARY -> tickScary(player);
|
||||
case REAL -> tickReal(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void tickEyes(PlayerEntity player) {
|
||||
if (hasActed) return;
|
||||
|
||||
// Slowly approach player
|
||||
Vec3d toPlayer = player.getPos().subtract(this.getPos()).normalize();
|
||||
this.setVelocity(toPlayer.multiply(0.05));
|
||||
this.velocityModified = true;
|
||||
|
||||
// Look at player
|
||||
this.getLookControl().lookAt(player, 30f, 30f);
|
||||
|
||||
// When close enough, flashbang
|
||||
double dist = this.squaredDistanceTo(player);
|
||||
if (dist < 4.0) {
|
||||
hasActed = true;
|
||||
if (player instanceof ServerPlayerEntity sp) {
|
||||
SmilerEffectManager.triggerFlashbang(sp);
|
||||
}
|
||||
// Notify spawn manager
|
||||
SmilerSpawnManager.onSmilerActed(player.getUuid(), SmilerType.EYES);
|
||||
this.discard();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickScary(PlayerEntity player) {
|
||||
if (hasActed) return;
|
||||
|
||||
boolean playerLooking = isPlayerLookingAt(player);
|
||||
|
||||
if (!scaryTeleported) {
|
||||
// Stay still while player is looking
|
||||
if (playerLooking) {
|
||||
playerWasLooking = true;
|
||||
// Don't move
|
||||
this.setVelocity(Vec3d.ZERO);
|
||||
} else if (playerWasLooking) {
|
||||
// Player looked away — teleport close
|
||||
Vec3d behindPlayer = player.getPos()
|
||||
.add(player.getRotationVector().multiply(-1.5));
|
||||
this.teleport(behindPlayer.x, behindPlayer.y, behindPlayer.z);
|
||||
scaryTeleported = true;
|
||||
}
|
||||
} else {
|
||||
// Teleported — wait for player to look at us again
|
||||
if (playerLooking) {
|
||||
hasActed = true;
|
||||
if (player instanceof ServerPlayerEntity sp) {
|
||||
SmilerEffectManager.triggerJumpscare(sp, SmilerType.SCARY);
|
||||
float damage = 2.0f + getWorld().random.nextFloat() * 4.0f; // 2-6 hearts = 4-12 hp
|
||||
player.damage(getWorld().getDamageSources().mobAttack(this), damage * 2);
|
||||
}
|
||||
SmilerSpawnManager.onSmilerActed(player.getUuid(), SmilerType.SCARY);
|
||||
this.discard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tickReal(PlayerEntity player) {
|
||||
if (hasActed) return;
|
||||
|
||||
// Run directly at player
|
||||
Vec3d toPlayer = player.getPos().subtract(this.getPos()).normalize();
|
||||
this.setVelocity(toPlayer.multiply(0.4));
|
||||
this.velocityModified = true;
|
||||
this.getLookControl().lookAt(player, 30f, 30f);
|
||||
|
||||
double dist = this.squaredDistanceTo(player);
|
||||
if (dist < 4.0) {
|
||||
hasActed = true;
|
||||
if (player instanceof ServerPlayerEntity sp) {
|
||||
SmilerEffectManager.triggerJumpscare(sp, SmilerType.REAL);
|
||||
float damage = 4.0f + getWorld().random.nextFloat() * 4.0f; // 4-8 hearts = 8-16 hp
|
||||
player.damage(getWorld().getDamageSources().mobAttack(this), damage * 2);
|
||||
}
|
||||
SmilerSpawnManager.onSmilerActed(player.getUuid(), SmilerType.REAL);
|
||||
this.discard();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPlayerLookingAt(PlayerEntity player) {
|
||||
Vec3d eyePos = player.getEyePos();
|
||||
Vec3d lookVec = player.getRotationVector();
|
||||
Vec3d toEntity = this.getPos().subtract(eyePos).normalize();
|
||||
double dot = lookVec.dotProduct(toEntity);
|
||||
return dot > 0.97; // ~14 degree cone
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCustomDataToNbt(NbtCompound nbt) {
|
||||
super.writeCustomDataToNbt(nbt);
|
||||
nbt.putString("SmilerType", smilerType.name());
|
||||
if (targetPlayerUUID != null) {
|
||||
nbt.putUuid("TargetPlayer", targetPlayerUUID);
|
||||
}
|
||||
nbt.putBoolean("HasActed", hasActed);
|
||||
nbt.putBoolean("ScaryTeleported", scaryTeleported);
|
||||
nbt.putBoolean("PlayerWasLooking", playerWasLooking);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readCustomDataFromNbt(NbtCompound nbt) {
|
||||
super.readCustomDataFromNbt(nbt);
|
||||
if (nbt.contains("SmilerType")) {
|
||||
smilerType = SmilerType.valueOf(nbt.getString("SmilerType"));
|
||||
}
|
||||
if (nbt.containsUuid("TargetPlayer")) {
|
||||
targetPlayerUUID = nbt.getUuid("TargetPlayer");
|
||||
}
|
||||
hasActed = nbt.getBoolean("HasActed");
|
||||
scaryTeleported = nbt.getBoolean("ScaryTeleported");
|
||||
playerWasLooking = nbt.getBoolean("PlayerWasLooking");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvisible() { return false; }
|
||||
|
||||
@Override
|
||||
public boolean canTarget(EntityType<?> type) { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean canStartRiding(Entity entity) { return false; }
|
||||
|
||||
@Override
|
||||
public boolean isPushable() { return false; }
|
||||
}
|
||||
131
src/main/java/dev/tggamesyt/szar/SmilerSpawnManager.java
Normal file
131
src/main/java/dev/tggamesyt/szar/SmilerSpawnManager.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SmilerSpawnManager {
|
||||
|
||||
// Per player, per type: world time when cooldown started
|
||||
private static final Map<UUID, Map<SmilerType, Long>> cooldowns = new HashMap<>();
|
||||
// Per player, per type: is there currently a live smiler of this type
|
||||
private static final Map<UUID, Set<SmilerType>> activeSmilers = new HashMap<>();
|
||||
|
||||
private static final long COOLDOWN_TICKS = 1200; // 1 minute
|
||||
// Random spawn check every 3-8 seconds per player
|
||||
private static int spawnCheckTimer = 0;
|
||||
|
||||
public static void register() {
|
||||
ServerTickEvents.END_SERVER_TICK.register(SmilerSpawnManager::tick);
|
||||
}
|
||||
|
||||
private static void tick(MinecraftServer server) {
|
||||
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
|
||||
if (backrooms == null) return;
|
||||
|
||||
// Only spawn during blackout
|
||||
if (BackroomsLightManager.currentEvent != BackroomsLightManager.GlobalEvent.BLACKOUT) {
|
||||
// Clean up any lingering smilers if blackout ended
|
||||
return;
|
||||
}
|
||||
|
||||
spawnCheckTimer--;
|
||||
if (spawnCheckTimer > 0) return;
|
||||
spawnCheckTimer = 60 + backrooms.random.nextInt(100); // 3-8 seconds
|
||||
|
||||
for (ServerPlayerEntity player : backrooms.getPlayers()) {
|
||||
UUID uuid = player.getUuid();
|
||||
long now = backrooms.getTime();
|
||||
|
||||
for (SmilerType type : SmilerType.values()) {
|
||||
// Skip if already active
|
||||
if (isActive(uuid, type)) continue;
|
||||
|
||||
// Skip if on cooldown
|
||||
if (isOnCooldown(uuid, type, now)) continue;
|
||||
|
||||
// 40% chance to actually spawn this type this check
|
||||
if (backrooms.random.nextFloat() > 0.4f) continue;
|
||||
|
||||
spawnSmiler(backrooms, player, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void spawnSmiler(ServerWorld world, ServerPlayerEntity player, SmilerType type) {
|
||||
// Find a spawn position in the backrooms — in a corridor, 15-30 blocks from player
|
||||
Vec3d spawnPos = findSpawnPos(world, player);
|
||||
if (spawnPos == null) return;
|
||||
|
||||
SmilerEntity entity = new SmilerEntity(Szar.SMILER_ENTITY_TYPE, world);
|
||||
entity.smilerType = type;
|
||||
entity.setTargetPlayer(player);
|
||||
entity.refreshPositionAndAngles(spawnPos.x, spawnPos.y, spawnPos.z, 0f, 0f);
|
||||
world.spawnEntity(entity);
|
||||
|
||||
markActive(player.getUuid(), type);
|
||||
}
|
||||
|
||||
private static Vec3d findSpawnPos(ServerWorld world, PlayerEntity player) {
|
||||
// Try to find an open spot 15-30 blocks away from player
|
||||
for (int attempt = 0; attempt < 20; attempt++) {
|
||||
double angle = world.random.nextDouble() * Math.PI * 2;
|
||||
double dist = 15 + world.random.nextDouble() * 15;
|
||||
double x = player.getX() + Math.cos(angle) * dist;
|
||||
double z = player.getZ() + Math.sin(angle) * dist;
|
||||
double y = 6; // backrooms floor level + 1
|
||||
|
||||
BlockPos pos = new BlockPos((int) x, (int) y, (int) z);
|
||||
// Check it's open space
|
||||
if (world.getBlockState(pos).isAir()
|
||||
&& world.getBlockState(pos.up()).isAir()) {
|
||||
return new Vec3d(x, y, z);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void onSmilerActed(UUID playerUUID, SmilerType type) {
|
||||
markInactive(playerUUID, type);
|
||||
// Start cooldown
|
||||
// We don't have world time here so use system time approximation
|
||||
// Actually store it via a flag and check in tick
|
||||
cooldowns.computeIfAbsent(playerUUID, k -> new HashMap<>())
|
||||
.put(type, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static void onSmilerDied(UUID playerUUID, SmilerType type) {
|
||||
markInactive(playerUUID, type);
|
||||
cooldowns.computeIfAbsent(playerUUID, k -> new HashMap<>())
|
||||
.put(type, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private static boolean isOnCooldown(UUID uuid, SmilerType type, long worldTime) {
|
||||
Map<SmilerType, Long> playerCooldowns = cooldowns.get(uuid);
|
||||
if (playerCooldowns == null) return false;
|
||||
Long cooldownStart = playerCooldowns.get(type);
|
||||
if (cooldownStart == null) return false;
|
||||
// Use milliseconds: 1 minute = 60000ms
|
||||
return (System.currentTimeMillis() - cooldownStart) < 60000;
|
||||
}
|
||||
|
||||
private static boolean isActive(UUID uuid, SmilerType type) {
|
||||
Set<SmilerType> active = activeSmilers.get(uuid);
|
||||
return active != null && active.contains(type);
|
||||
}
|
||||
|
||||
private static void markActive(UUID uuid, SmilerType type) {
|
||||
activeSmilers.computeIfAbsent(uuid, k -> new HashSet<>()).add(type);
|
||||
}
|
||||
|
||||
private static void markInactive(UUID uuid, SmilerType type) {
|
||||
Set<SmilerType> active = activeSmilers.get(uuid);
|
||||
if (active != null) active.remove(type);
|
||||
}
|
||||
}
|
||||
5
src/main/java/dev/tggamesyt/szar/SmilerType.java
Normal file
5
src/main/java/dev/tggamesyt/szar/SmilerType.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package dev.tggamesyt.szar;
|
||||
|
||||
public enum SmilerType {
|
||||
EYES, SCARY, REAL
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
|
||||
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
||||
@@ -369,6 +371,7 @@ public class Szar implements ModInitializer {
|
||||
entries.add(Szar.WALL_BOTTOM_ITEM);
|
||||
entries.add(Szar.CEILING_ITEM);
|
||||
entries.add(Szar.PLASTIC_ITEM);
|
||||
entries.add(Szar.BACKROOMS_LIGHT_ITEM);
|
||||
entries.add(Szar.BEAN);
|
||||
entries.add(Szar.CAN_OF_BEANS);
|
||||
entries.add(Szar.ALMOND_WATER);
|
||||
@@ -389,6 +392,7 @@ public class Szar implements ModInitializer {
|
||||
entries.add(Szar.WEED_ITEM);
|
||||
entries.add(Szar.WEED_JOINT_ITEM);
|
||||
entries.add(Szar.CHEMICAL_WORKBENCH_ITEM);
|
||||
entries.add(Szar.BEER);
|
||||
// war guys
|
||||
entries.add(Szar.HITTER_SPAWNEGG);
|
||||
entries.add(Szar.NAZI_SPAWNEGG);
|
||||
@@ -793,6 +797,10 @@ public class Szar implements ModInitializer {
|
||||
TERRORIST_ENTITY_TYPE,
|
||||
IslamTerrorist.createAttributes()
|
||||
);
|
||||
FabricDefaultAttributeRegistry.register(
|
||||
SMILER_ENTITY_TYPE,
|
||||
SmilerEntity.createAttributes()
|
||||
);
|
||||
SpawnRestriction.register(
|
||||
Szar.PoliceEntityType,
|
||||
SpawnRestriction.Location.ON_GROUND, // spawn on solid blocks
|
||||
@@ -1175,6 +1183,30 @@ public class Szar implements ModInitializer {
|
||||
);
|
||||
BackroomsBarrelManager.register();
|
||||
BackroomsLightManager.register();
|
||||
SmilerSpawnManager.register();
|
||||
// 🔄 Dimension change
|
||||
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> {
|
||||
if (origin.getRegistryKey() == Szar.BACKROOMS_KEY) {
|
||||
restoreIfNeeded(player);
|
||||
}
|
||||
});
|
||||
|
||||
// 💀 Death (handled on respawn copy)
|
||||
ServerPlayerEvents.COPY_FROM.register((oldPlayer, newPlayer, alive) -> {
|
||||
if (!alive) {
|
||||
restoreIfNeeded(newPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
// 🔌 Join (failsafe)
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
ServerPlayerEntity player = handler.getPlayer();
|
||||
|
||||
if (player.getWorld().getRegistryKey() != Szar.BACKROOMS_KEY) {
|
||||
restoreIfNeeded(player);
|
||||
}
|
||||
});
|
||||
ServerTickEvents.END_SERVER_TICK.register(DrunkEffect::tick);
|
||||
}
|
||||
// Blocks
|
||||
public static final TrackerBlock TRACKER_BLOCK = Registry.register(
|
||||
@@ -1213,7 +1245,7 @@ public class Szar implements ModInitializer {
|
||||
);
|
||||
public static final WallBlock WALL_BLOCK = Registry.register(
|
||||
Registries.BLOCK, new Identifier(MOD_ID, "wall"),
|
||||
new WallBlock(AbstractBlock.Settings.create())
|
||||
new WallBlock(AbstractBlock.Settings.create().strength(-1.0f, 3600000f))
|
||||
);
|
||||
public static final BlockEntityType<WallBlockEntity> WALL_BLOCK_ENTITY = Registry.register(
|
||||
Registries.BLOCK_ENTITY_TYPE, new Identifier(MOD_ID, "wall"),
|
||||
@@ -1221,15 +1253,15 @@ public class Szar implements ModInitializer {
|
||||
);
|
||||
public static final Block WALL_BOTTOM_BLOCK = Registry.register(
|
||||
Registries.BLOCK, new Identifier(MOD_ID, "wall_bottom"),
|
||||
new Block(AbstractBlock.Settings.create())
|
||||
new Block(AbstractBlock.Settings.create().strength(-1.0f, 3600000f))
|
||||
);
|
||||
public static final Block CEILING = Registry.register(
|
||||
Registries.BLOCK, new Identifier(MOD_ID, "ceiling"),
|
||||
new Block(AbstractBlock.Settings.create())
|
||||
new Block(AbstractBlock.Settings.create().strength(-1.0f, 3600000f))
|
||||
);
|
||||
public static final Block PLASTIC = Registry.register(
|
||||
Registries.BLOCK, new Identifier(MOD_ID, "plastic"),
|
||||
new Block(AbstractBlock.Settings.create())
|
||||
new Block(AbstractBlock.Settings.create().strength(-1.0f, 3600000f))
|
||||
);
|
||||
public static final Item WALL_ITEM = Registry.register(
|
||||
Registries.ITEM,
|
||||
@@ -1805,14 +1837,18 @@ public class Szar implements ModInitializer {
|
||||
public static final Item ALMOND_WATER = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(MOD_ID, "almond_water"),
|
||||
new AlmondWaterItem(new Item.Settings())
|
||||
new AlmondWaterItem(new Item.Settings().maxCount(16))
|
||||
);
|
||||
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))
|
||||
.luminance(state -> {
|
||||
return switch (state.get(BackroomsLightBlock.LIGHT_STATE)) {
|
||||
case ON, FLICKERING -> 15;
|
||||
case OFF -> 0;
|
||||
};
|
||||
})
|
||||
.strength(-1.0f, 3600000f))
|
||||
);
|
||||
|
||||
public static final BlockItem BACKROOMS_LIGHT_ITEM = Registry.register(
|
||||
@@ -1827,6 +1863,25 @@ public class Szar implements ModInitializer {
|
||||
FabricBlockEntityTypeBuilder.create(BackroomsLightBlockEntity::new,
|
||||
BACKROOMS_LIGHT).build()
|
||||
);
|
||||
|
||||
public static final EntityType<SmilerEntity> SMILER_ENTITY_TYPE = Registry.register(
|
||||
Registries.ENTITY_TYPE,
|
||||
new Identifier(MOD_ID, "smiler"),
|
||||
FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, SmilerEntity::new)
|
||||
.dimensions(EntityDimensions.fixed(1.0f, 1.5f))
|
||||
.build()
|
||||
);
|
||||
public static final DrunkEffect DRUNK_EFFECT = Registry.register(
|
||||
Registries.STATUS_EFFECT,
|
||||
new Identifier(MOD_ID, "drunk"),
|
||||
new DrunkEffect()
|
||||
);
|
||||
public static final Item BEER = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(MOD_ID, "beer"),
|
||||
new BeerItem(new Item.Settings().maxCount(16))
|
||||
);
|
||||
|
||||
public static final SoundEvent BAITER =
|
||||
SoundEvent.of(new Identifier(MOD_ID, "baiter"));
|
||||
public static final Item BAITER_DISC = Registry.register(
|
||||
@@ -2176,5 +2231,41 @@ public class Szar implements ModInitializer {
|
||||
player.getWorld().playSound(null, player.getBlockPos(),
|
||||
Szar.REVOLVER_CLICK2_SOUND, SoundCategory.PLAYERS, 1f, pitch);
|
||||
}
|
||||
|
||||
private static void restoreIfNeeded(ServerPlayerEntity player) {
|
||||
MinecraftServer server = player.getServer();
|
||||
if (server == null) return;
|
||||
|
||||
ServerWorld overworld = server.getWorld(World.OVERWORLD);
|
||||
if (overworld == null) return;
|
||||
|
||||
PortalDataState state = PortalDataState.getOrCreate(overworld);
|
||||
NbtCompound tag = state.getOrCreatePlayerData(player.getUuid());
|
||||
|
||||
// 🔑 THIS is the only condition that matters
|
||||
if (!tag.contains("SzarSavedInventory")) return;
|
||||
|
||||
// ✅ Restore inventory
|
||||
PortalBlock.restoreInventory(player, server);
|
||||
|
||||
// 🧹 CLEANUP tracker using YOUR existing data
|
||||
if (tag.contains("OwnerTrackerX")) {
|
||||
BlockPos pos = new BlockPos(
|
||||
tag.getInt("OwnerTrackerX"),
|
||||
tag.getInt("OwnerTrackerY"),
|
||||
tag.getInt("OwnerTrackerZ")
|
||||
);
|
||||
|
||||
if (overworld.getBlockEntity(pos) instanceof TrackerBlockEntity tracker) {
|
||||
TrackerBlockEntity root = tracker.getRoot(overworld);
|
||||
root.removePlayer(player.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
// 🧽 Clean up leftover data
|
||||
tag.remove("OwnerTrackerX");
|
||||
tag.remove("OwnerTrackerY");
|
||||
tag.remove("OwnerTrackerZ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"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" }
|
||||
"light_state=flickering": { "model": "szar:block/backrooms_light_on" }
|
||||
}
|
||||
}
|
||||
@@ -149,5 +149,8 @@
|
||||
"item.szar.bean": "Bean",
|
||||
"item.szar.can_of_beans": "Can of Beans",
|
||||
"item.szar.almond_water": "Almond Water",
|
||||
"block.szar.backrooms_light": "Light"
|
||||
"block.szar.backrooms_light": "Light",
|
||||
"entity.szar.smiler": "Smiler",
|
||||
"item.szar.beer": "Beer",
|
||||
"effect.szar.drunk": "Drunk"
|
||||
}
|
||||
|
||||
6
src/main/resources/assets/szar/models/item/beer.json
Normal file
6
src/main/resources/assets/szar/models/item/beer.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "szar:item/beer"
|
||||
}
|
||||
}
|
||||
@@ -202,5 +202,29 @@
|
||||
"stream": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"real": {
|
||||
"sounds": [
|
||||
{
|
||||
"name": "szar:real",
|
||||
"stream": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"scary": {
|
||||
"sounds": [
|
||||
{
|
||||
"name": "szar:scary",
|
||||
"stream": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"flashbang": {
|
||||
"sounds": [
|
||||
{
|
||||
"name": "szar:flashbang",
|
||||
"stream": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/main/resources/assets/szar/sounds/flashbang.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/flashbang.ogg
Normal file
Binary file not shown.
BIN
src/main/resources/assets/szar/sounds/real.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/real.ogg
Normal file
Binary file not shown.
BIN
src/main/resources/assets/szar/sounds/scary.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/scary.ogg
Normal file
Binary file not shown.
BIN
src/main/resources/assets/szar/textures/entity/eyes.png
Normal file
BIN
src/main/resources/assets/szar/textures/entity/eyes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/assets/szar/textures/entity/real.png
Normal file
BIN
src/main/resources/assets/szar/textures/entity/real.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
BIN
src/main/resources/assets/szar/textures/entity/scary.png
Normal file
BIN
src/main/resources/assets/szar/textures/entity/scary.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
BIN
src/main/resources/assets/szar/textures/item/beer.png
Normal file
BIN
src/main/resources/assets/szar/textures/item/beer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/main/resources/assets/szar/textures/mob_effect/drunk.png
Normal file
BIN
src/main/resources/assets/szar/textures/mob_effect/drunk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
20
src/main/resources/data/szar/recipes/beer.json
Normal file
20
src/main/resources/data/szar/recipes/beer.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" W ",
|
||||
"WPW",
|
||||
" W "
|
||||
],
|
||||
"key": {
|
||||
"W": {
|
||||
"item": "minecraft:wheat"
|
||||
},
|
||||
"P": {
|
||||
"item": "minecraft:potion"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "szar:beer",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user