forradalom es szabadsagharc: island structure, update ak47, rework revolver
@@ -6,7 +6,7 @@ minecraft_version=1.20.1
|
|||||||
yarn_mappings=1.20.1+build.10
|
yarn_mappings=1.20.1+build.10
|
||||||
loader_version=0.18.3
|
loader_version=0.18.3
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=26.3.14
|
mod_version=26.3.15
|
||||||
maven_group=dev.tggamesyt
|
maven_group=dev.tggamesyt
|
||||||
archives_base_name=szar
|
archives_base_name=szar
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
|
public class AK47InputState {
|
||||||
|
public static boolean mouseHeld = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
|
import dev.tggamesyt.szar.RevolverItem;
|
||||||
|
import dev.tggamesyt.szar.Szar;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
public class RevolverHudRenderer {
|
||||||
|
|
||||||
|
private static final int RADIUS = 30;
|
||||||
|
private static final int CX_FROM_RIGHT = 80;
|
||||||
|
private static final int CY_FROM_BOTTOM = 80;
|
||||||
|
private static final float SNAP_SPEED = 0.15F;
|
||||||
|
private static final float SLIDE_SPEED = 0.18F;
|
||||||
|
// How far off screen to the right the barrel slides when closed
|
||||||
|
private static final int SLIDE_DISTANCE = 150;
|
||||||
|
|
||||||
|
private static float displayAngle = 0F;
|
||||||
|
private static int lastKnownChamber = 0;
|
||||||
|
|
||||||
|
// 0 = fully hidden (off screen right), 1 = fully visible
|
||||||
|
private static float slideProgress = 0F;
|
||||||
|
|
||||||
|
public static void register() {
|
||||||
|
HudRenderCallback.EVENT.register(RevolverHudRenderer::render);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick(int currentChamber, float tickDelta) {
|
||||||
|
float degreesPerChamber = 360F / RevolverItem.CHAMBERS;
|
||||||
|
float targetAngle = -currentChamber * degreesPerChamber;
|
||||||
|
|
||||||
|
if (currentChamber != lastKnownChamber) {
|
||||||
|
lastKnownChamber = currentChamber;
|
||||||
|
}
|
||||||
|
|
||||||
|
float delta = targetAngle - displayAngle;
|
||||||
|
while (delta > 180F) delta -= 360F;
|
||||||
|
while (delta < -180F) delta += 360F;
|
||||||
|
displayAngle += delta * SNAP_SPEED * tickDelta;
|
||||||
|
|
||||||
|
// Slide in/out
|
||||||
|
float targetSlide = RevolverHudState.isOpen ? 1F : 0F;
|
||||||
|
slideProgress += (targetSlide - slideProgress) * SLIDE_SPEED * tickDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void render(DrawContext context, float tickDelta) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client.player == null) return;
|
||||||
|
|
||||||
|
ItemStack stack = client.player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.REVOLVER)) return;
|
||||||
|
|
||||||
|
int current = RevolverItem.getCurrentChamber(stack);
|
||||||
|
tick(current, tickDelta);
|
||||||
|
|
||||||
|
// Don't render at all if fully hidden
|
||||||
|
if (slideProgress < 0.01F) return;
|
||||||
|
|
||||||
|
boolean[] chambers = RevolverItem.getChambers(stack);
|
||||||
|
|
||||||
|
int baseCx = context.getScaledWindowWidth() - CX_FROM_RIGHT;
|
||||||
|
int cy = context.getScaledWindowHeight() - CY_FROM_BOTTOM;
|
||||||
|
|
||||||
|
// Slide offset: 0 = off screen right, 1 = in place
|
||||||
|
int slideOffset = (int)((1F - slideProgress) * SLIDE_DISTANCE);
|
||||||
|
int cx = baseCx + slideOffset;
|
||||||
|
|
||||||
|
// Draw translucent background circle
|
||||||
|
int circleAlpha = (int)(slideProgress * 120); // max alpha 120 (~47% opacity)
|
||||||
|
drawCircle(context, cx, cy, RADIUS + 8, circleAlpha);
|
||||||
|
|
||||||
|
// Draw chamber dots
|
||||||
|
for (int i = 0; i < RevolverItem.CHAMBERS; i++) {
|
||||||
|
float baseAngle = (float)(-Math.PI / 2.0)
|
||||||
|
+ i * (float)(2.0 * Math.PI / RevolverItem.CHAMBERS)
|
||||||
|
+ (float)Math.toRadians(displayAngle);
|
||||||
|
|
||||||
|
int x = (int)(cx + Math.cos(baseAngle) * RADIUS);
|
||||||
|
int y = (int)(cy + Math.sin(baseAngle) * RADIUS);
|
||||||
|
|
||||||
|
boolean isCurrent = i == current;
|
||||||
|
int size = isCurrent ? 6 : 4;
|
||||||
|
int color;
|
||||||
|
if (isCurrent) {
|
||||||
|
color = (int)(slideProgress * 255) << 24 | (chambers[i] ? 0x0000FF00 : 0x00FF4444);
|
||||||
|
} else {
|
||||||
|
color = (int)(slideProgress * 255) << 24 | (chambers[i] ? 0x00FFFFFF : 0x00555555);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.fill(x - size / 2, y - size / 2, x + size / 2, y + size / 2, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center dot
|
||||||
|
int dotAlpha = (int)(slideProgress * 255);
|
||||||
|
context.fill(cx - 2, cy - 2, cx + 2, cy + 2, dotAlpha << 24 | 0x00AAAAAA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawCircle(DrawContext context, int cx, int cy, int radius, int alpha) {
|
||||||
|
// Approximate circle with filled quads at each degree
|
||||||
|
int steps = 64;
|
||||||
|
int color = alpha << 24 | 0x00222222;
|
||||||
|
for (int i = 0; i < steps; i++) {
|
||||||
|
float a1 = (float)(i * 2 * Math.PI / steps);
|
||||||
|
float a2 = (float)((i + 1) * 2 * Math.PI / steps);
|
||||||
|
|
||||||
|
// Draw thin triangle slice as a quad
|
||||||
|
int x1 = (int)(cx + Math.cos(a1) * radius);
|
||||||
|
int y1 = (int)(cy + Math.sin(a1) * radius);
|
||||||
|
int x2 = (int)(cx + Math.cos(a2) * radius);
|
||||||
|
int y2 = (int)(cy + Math.sin(a2) * radius);
|
||||||
|
|
||||||
|
// Fill triangle from center to edge
|
||||||
|
context.fill(Math.min(x1, x2) - 1, Math.min(y1, y2) - 1,
|
||||||
|
Math.max(x1, x2) + 1, Math.max(y1, y2) + 1, color);
|
||||||
|
}
|
||||||
|
// Fill center solid
|
||||||
|
context.fill(cx - radius, cy - radius, cx + radius, cy + radius, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
|
public class RevolverHudState {
|
||||||
|
public static boolean isOpen = false;
|
||||||
|
public static boolean isSpinning = false;
|
||||||
|
public static float spinAngle = 0F;
|
||||||
|
public static float spinVelocity = 0F;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
|
import dev.tggamesyt.szar.RevolverItem;
|
||||||
|
|
||||||
|
public class RevolverHudTicker {
|
||||||
|
|
||||||
|
private static final float DECELERATION = 0.88F;
|
||||||
|
private static final float STOP_THRESHOLD = 0.2F;
|
||||||
|
|
||||||
|
public static void tick() {
|
||||||
|
if (!RevolverHudState.isSpinning) return;
|
||||||
|
|
||||||
|
RevolverHudState.spinAngle += RevolverHudState.spinVelocity;
|
||||||
|
RevolverHudState.spinVelocity *= DECELERATION;
|
||||||
|
|
||||||
|
if (Math.abs(RevolverHudState.spinVelocity) < STOP_THRESHOLD) {
|
||||||
|
RevolverHudState.isSpinning = false;
|
||||||
|
RevolverHudState.spinAngle = 0F;
|
||||||
|
RevolverHudState.spinVelocity = 0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startSpin(int steps) {
|
||||||
|
// Spin in the positive direction, one full rotation per step minimum
|
||||||
|
RevolverHudState.spinAngle = 0F;
|
||||||
|
RevolverHudState.spinVelocity = steps * (360F / RevolverItem.CHAMBERS) * 0.25F;
|
||||||
|
RevolverHudState.isSpinning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
package dev.tggamesyt.szar.client;
|
|
||||||
|
|
||||||
import dev.tggamesyt.szar.RevolverItem;
|
|
||||||
import dev.tggamesyt.szar.Szar;
|
|
||||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
|
||||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.gui.DrawContext;
|
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
|
||||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
|
|
||||||
public class RevolverScreen extends Screen {
|
|
||||||
|
|
||||||
private final ItemStack revolverStack;
|
|
||||||
|
|
||||||
private static final int[][] SLOT_OFFSETS = {
|
|
||||||
{ 0, -50}, {43, -25}, {43, 25},
|
|
||||||
{ 0, 50}, {-43, 25}, {-43,-25}
|
|
||||||
};
|
|
||||||
|
|
||||||
public RevolverScreen(ItemStack stack) {
|
|
||||||
super(Text.literal("Revolver"));
|
|
||||||
this.revolverStack = stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init() {
|
|
||||||
int cx = this.width / 2;
|
|
||||||
int cy = this.height / 2;
|
|
||||||
|
|
||||||
boolean[] chambers = RevolverItem.getChambers(revolverStack);
|
|
||||||
|
|
||||||
for (int i = 0; i < RevolverItem.CHAMBERS; i++) {
|
|
||||||
final int index = i;
|
|
||||||
int bx = cx + SLOT_OFFSETS[i][0] - 15;
|
|
||||||
int by = cy + SLOT_OFFSETS[i][1] - 10;
|
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(
|
|
||||||
getChamberText(index, chambers),
|
|
||||||
btn -> {
|
|
||||||
PlayerEntity player = MinecraftClient.getInstance().player;
|
|
||||||
if (player == null) return;
|
|
||||||
|
|
||||||
boolean[] current = RevolverItem.getChambers(revolverStack);
|
|
||||||
|
|
||||||
if (current[index]) {
|
|
||||||
// Optimistically update client visual
|
|
||||||
current[index] = false;
|
|
||||||
RevolverItem.setChambers(revolverStack, current);
|
|
||||||
} else {
|
|
||||||
// Check if player has bullet before sending — purely for visual feedback
|
|
||||||
boolean hasBullet = false;
|
|
||||||
for (int o = 0; o < player.getInventory().size(); o++) {
|
|
||||||
ItemStack s = player.getInventory().getStack(o);
|
|
||||||
if (!s.isEmpty() && s.isOf(Szar.BULLET_ITEM)) {
|
|
||||||
hasBullet = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasBullet) return; // don't even send packet
|
|
||||||
|
|
||||||
current[index] = true;
|
|
||||||
RevolverItem.setChambers(revolverStack, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send to server — server does the actual inventory changes
|
|
||||||
PacketByteBuf buf = PacketByteBufs.create();
|
|
||||||
buf.writeInt(index);
|
|
||||||
buf.writeBoolean(!current[index]); // wasLoaded = what it WAS before the flip
|
|
||||||
ClientPlayNetworking.send(Szar.REVOLVER_CHAMBER_CHANGE, buf);
|
|
||||||
|
|
||||||
clearChildren();
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
).dimensions(bx, by, 30, 20).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(
|
|
||||||
Text.literal("Done"),
|
|
||||||
btn -> this.close()
|
|
||||||
).dimensions(cx - 40, cy + 75, 80, 20).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean takeBullet(PlayerEntity player) {
|
|
||||||
for (int i = 0; i < player.getInventory().size(); i++) {
|
|
||||||
ItemStack s = player.getInventory().getStack(i);
|
|
||||||
if (!s.isEmpty() && s.isOf(Szar.BULLET_ITEM)) {
|
|
||||||
s.decrement(1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Text getChamberText(int i, boolean[] chambers) {
|
|
||||||
int current = RevolverItem.getCurrentChamber(revolverStack);
|
|
||||||
String prefix = (i == current) ? "►" : " ";
|
|
||||||
return Text.literal(prefix + (chambers[i] ? "●" : "○"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
this.renderBackground(context);
|
|
||||||
context.drawCenteredTextWithShadow(this.textRenderer,
|
|
||||||
Text.literal("Load Revolver"), this.width / 2, this.height / 2 - 70, 0xFFFFFF);
|
|
||||||
context.drawCenteredTextWithShadow(this.textRenderer,
|
|
||||||
Text.literal("► = current chamber"), this.width / 2, this.height / 2 - 58, 0xAAAAAA);
|
|
||||||
super.render(context, mouseX, mouseY, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldPause() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void syncToServer() {
|
|
||||||
boolean[] chambers = RevolverItem.getChambers(revolverStack);
|
|
||||||
int current = RevolverItem.getCurrentChamber(revolverStack);
|
|
||||||
|
|
||||||
PacketByteBuf buf = PacketByteBufs.create();
|
|
||||||
for (int i = 0; i < RevolverItem.CHAMBERS; i++) {
|
|
||||||
buf.writeBoolean(chambers[i]);
|
|
||||||
}
|
|
||||||
buf.writeInt(current);
|
|
||||||
|
|
||||||
ClientPlayNetworking.send(Szar.REVOLVER_SYNC, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package dev.tggamesyt.szar.client;
|
|||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import dev.tggamesyt.szar.*;
|
import dev.tggamesyt.szar.*;
|
||||||
import dev.tggamesyt.szar.ServerCosmetics.NameType;
|
import dev.tggamesyt.szar.ServerCosmetics.NameType;
|
||||||
|
import dev.tggamesyt.szar.client.mixin.RevolverAttackMixin;
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||||
@@ -91,6 +92,20 @@ public class SzarClient implements ClientModInitializer {
|
|||||||
);
|
);
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
|
ClientTickEvents.START_CLIENT_TICK.register(client -> {
|
||||||
|
if (!AK47InputState.mouseHeld) return;
|
||||||
|
if (client.player == null || client.currentScreen != null) {
|
||||||
|
AK47InputState.mouseHeld = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ItemStack stack = client.player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.AK47)) {
|
||||||
|
AK47InputState.mouseHeld = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!client.player.isUsingItem()) return;
|
||||||
|
ClientPlayNetworking.send(Szar.AK47_SHOOT, PacketByteBufs.create());
|
||||||
|
});
|
||||||
BulletDecalRenderer.register();
|
BulletDecalRenderer.register();
|
||||||
// In ClientModInitializer:
|
// In ClientModInitializer:
|
||||||
ClientPlayNetworking.registerGlobalReceiver(Szar.BULLET_IMPACT, (client, handler, buf, sender) -> {
|
ClientPlayNetworking.registerGlobalReceiver(Szar.BULLET_IMPACT, (client, handler, buf, sender) -> {
|
||||||
@@ -103,17 +118,58 @@ public class SzarClient implements ClientModInitializer {
|
|||||||
BulletDecalStore.add(new Vec3d(x, y, z), face);
|
BulletDecalStore.add(new Vec3d(x, y, z), face);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Then in a ClientTickEvents.END_CLIENT_TICK:
|
|
||||||
|
RevolverHudRenderer.register();
|
||||||
|
|
||||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||||
if (SPIN_KEY.wasPressed() && client.player != null) {
|
RevolverHudTicker.tick();
|
||||||
|
|
||||||
|
if (client.player == null) return;
|
||||||
ItemStack stack = client.player.getMainHandStack();
|
ItemStack stack = client.player.getMainHandStack();
|
||||||
if (stack.isOf(Szar.REVOLVER)) {
|
|
||||||
// Send spin packet to server
|
if (!stack.isOf(Szar.REVOLVER)) {
|
||||||
ClientPlayNetworking.send(Szar.REVOLVER_SPIN,
|
RevolverHudState.isOpen = false;
|
||||||
PacketByteBufs.create());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SPIN_KEY.wasPressed()) {
|
||||||
|
if (client.player.isSneaking()) {
|
||||||
|
// Shift+R: toggle open/close
|
||||||
|
RevolverHudState.isOpen = !RevolverHudState.isOpen;
|
||||||
|
} else if (RevolverHudState.isOpen) {
|
||||||
|
// R while open: load/unload current chamber then advance
|
||||||
|
boolean[] chambers = RevolverItem.getChambers(stack);
|
||||||
|
int current = RevolverItem.getCurrentChamber(stack);
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
buf.writeInt(current);
|
||||||
|
buf.writeBoolean(chambers[current]); // wasLoaded
|
||||||
|
ClientPlayNetworking.send(Szar.REVOLVER_CHAMBER_CHANGE, buf);
|
||||||
|
} else {
|
||||||
|
// R while closed and not sneaking: spin
|
||||||
|
ClientPlayNetworking.send(Szar.REVOLVER_SPIN, PacketByteBufs.create());
|
||||||
|
// Animation starts when server responds with REVOLVER_SPIN_RESULT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ClientPlayNetworking.registerGlobalReceiver(Szar.REVOLVER_STATE_SYNC, (client, handler, buf, sender) -> {
|
||||||
|
boolean[] chambers = new boolean[RevolverItem.CHAMBERS];
|
||||||
|
for (int i = 0; i < RevolverItem.CHAMBERS; i++) {
|
||||||
|
chambers[i] = buf.readBoolean();
|
||||||
|
}
|
||||||
|
int current = buf.readInt();
|
||||||
|
|
||||||
|
client.execute(() -> {
|
||||||
|
if (client.player == null) return;
|
||||||
|
ItemStack stack = client.player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.REVOLVER)) return;
|
||||||
|
RevolverItem.setChambers(stack, chambers);
|
||||||
|
RevolverItem.setCurrentChamber(stack, current);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ClientPlayNetworking.registerGlobalReceiver(Szar.REVOLVER_SPIN_RESULT, (client2, handler, buf, sender) -> {
|
||||||
|
int steps = buf.readInt();
|
||||||
|
client2.execute(() -> RevolverHudTicker.startSpin(steps));
|
||||||
|
});
|
||||||
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
||||||
PacketByteBuf buf = PacketByteBufs.create();
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.tggamesyt.szar.client.mixin;
|
package dev.tggamesyt.szar.client.mixin;
|
||||||
|
|
||||||
|
import dev.tggamesyt.szar.AK47Item;
|
||||||
import dev.tggamesyt.szar.Joint;
|
import dev.tggamesyt.szar.Joint;
|
||||||
import dev.tggamesyt.szar.RevolverItem;
|
import dev.tggamesyt.szar.RevolverItem;
|
||||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||||
@@ -78,11 +79,46 @@ public abstract class HeldItemRendererMixin {
|
|||||||
matrices.push();
|
matrices.push();
|
||||||
|
|
||||||
// Center in middle of screen regardless of hand
|
// Center in middle of screen regardless of hand
|
||||||
|
if (!player.isSneaking()) {
|
||||||
matrices.translate(
|
matrices.translate(
|
||||||
isMainHand ? -0.18F : 0.18F,
|
isMainHand ? -0.18F : 0.18F,
|
||||||
-0.5F,
|
-0.5F,
|
||||||
-0.5F
|
-0.5F
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
matrices.translate(
|
||||||
|
isMainHand ? 1F : -1F,
|
||||||
|
-0.2F,
|
||||||
|
-0.5F
|
||||||
|
);
|
||||||
|
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(isMainHand ? 100.0F : -100F));
|
||||||
|
}
|
||||||
|
matrices.translate(0.0F, equipProgress * -0.6F, 0.0F);
|
||||||
|
|
||||||
|
HeldItemRenderer self = (HeldItemRenderer) (Object) this;
|
||||||
|
self.renderItem(player, item,
|
||||||
|
isRight ? ModelTransformationMode.FIRST_PERSON_RIGHT_HAND : ModelTransformationMode.FIRST_PERSON_LEFT_HAND,
|
||||||
|
!isRight, matrices, vertexConsumers, light);
|
||||||
|
|
||||||
|
matrices.pop();
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
if (item.getItem() instanceof AK47Item
|
||||||
|
&& player.isUsingItem()
|
||||||
|
&& player.getActiveHand() == hand) {
|
||||||
|
|
||||||
|
boolean isMainHand = hand == Hand.MAIN_HAND;
|
||||||
|
Arm arm = isMainHand ? player.getMainArm() : player.getMainArm().getOpposite();
|
||||||
|
boolean isRight = arm == Arm.RIGHT;
|
||||||
|
|
||||||
|
matrices.push();
|
||||||
|
|
||||||
|
// Center in middle of screen regardless of hand
|
||||||
|
matrices.translate(
|
||||||
|
0.00F,
|
||||||
|
-0.5F,
|
||||||
|
-1.0F
|
||||||
|
);
|
||||||
|
|
||||||
matrices.translate(0.0F, equipProgress * -0.6F, 0.0F);
|
matrices.translate(0.0F, equipProgress * -0.6F, 0.0F);
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package dev.tggamesyt.szar.client.mixin;
|
package dev.tggamesyt.szar.client.mixin;
|
||||||
|
|
||||||
import dev.tggamesyt.szar.Szar;
|
import dev.tggamesyt.szar.Szar;
|
||||||
import dev.tggamesyt.szar.client.RevolverScreen;
|
import dev.tggamesyt.szar.client.AK47InputState;
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.Mouse;
|
import net.minecraft.client.Mouse;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
@@ -19,20 +21,24 @@ public class RevolverAttackMixin {
|
|||||||
private void onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
|
private void onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
|
||||||
MinecraftClient client = MinecraftClient.getInstance();
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
if (client.player == null) return;
|
if (client.player == null) return;
|
||||||
if (client.currentScreen != null) return; // let screens handle their own clicks
|
if (client.currentScreen != null) return;
|
||||||
if (button != 0 || action != 1) return; // only left click press
|
if (button != 0) return;
|
||||||
|
|
||||||
ItemStack stack = client.player.getMainHandStack();
|
ItemStack stack = client.player.getMainHandStack();
|
||||||
if (!stack.isOf(Szar.REVOLVER)) return;
|
|
||||||
|
|
||||||
ci.cancel(); // cancel vanilla handling entirely
|
if (stack.isOf(Szar.REVOLVER)) {
|
||||||
|
if (action != 1) return;
|
||||||
if (!client.player.isUsingItem()) {
|
ci.cancel();
|
||||||
// Not aiming — open loading screen
|
if (client.player.isUsingItem()) {
|
||||||
client.execute(() -> client.setScreen(new RevolverScreen(stack)));
|
|
||||||
} else {
|
|
||||||
// Aiming — shoot
|
|
||||||
ClientPlayNetworking.send(Szar.REVOLVER_SHOOT, PacketByteBufs.create());
|
ClientPlayNetworking.send(Szar.REVOLVER_SHOOT, PacketByteBufs.create());
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.isOf(Szar.AK47)) {
|
||||||
|
ci.cancel();
|
||||||
|
if (action == 1) AK47InputState.mouseHeld = true;
|
||||||
|
if (action == 0) AK47InputState.mouseHeld = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package dev.tggamesyt.szar.client.mixin;
|
||||||
|
|
||||||
|
import dev.tggamesyt.szar.Szar;
|
||||||
|
import dev.tggamesyt.szar.client.RevolverHudState;
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.Mouse;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(Mouse.class)
|
||||||
|
public class RevolverScrollMixin {
|
||||||
|
|
||||||
|
@Inject(method = "onMouseScroll", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onScroll(long window, double horizontal, double vertical, CallbackInfo ci) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client.player == null || client.currentScreen != null) return;
|
||||||
|
|
||||||
|
ItemStack stack = client.player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.REVOLVER)) return;
|
||||||
|
if (!RevolverHudState.isOpen) return;
|
||||||
|
|
||||||
|
ci.cancel(); // don't scroll hotbar
|
||||||
|
|
||||||
|
int direction = vertical > 0 ? 1 : -1;
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
buf.writeInt(direction);
|
||||||
|
ClientPlayNetworking.send(Szar.REVOLVER_SCROLL, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"RadiatedItemRendererMixin",
|
"RadiatedItemRendererMixin",
|
||||||
"RadiationHeartMixin",
|
"RadiationHeartMixin",
|
||||||
"RevolverAttackMixin",
|
"RevolverAttackMixin",
|
||||||
|
"RevolverScrollMixin",
|
||||||
"ScreenFlipMixin",
|
"ScreenFlipMixin",
|
||||||
"SplashOverlayMixin",
|
"SplashOverlayMixin",
|
||||||
"TGcapeMixin",
|
"TGcapeMixin",
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package dev.tggamesyt.szar;
|
package dev.tggamesyt.szar;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.sound.SoundCategory;
|
|
||||||
import net.minecraft.sound.SoundEvents;
|
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.TypedActionResult;
|
import net.minecraft.util.TypedActionResult;
|
||||||
import net.minecraft.util.UseAction;
|
import net.minecraft.util.UseAction;
|
||||||
@@ -17,25 +14,7 @@ public class AK47Item extends Item {
|
|||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean consumeAmmo(PlayerEntity player) {
|
||||||
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
|
|
||||||
if (!(entity instanceof PlayerEntity player)) return;
|
|
||||||
if (!selected) return;
|
|
||||||
if (!player.isUsingItem()) return;
|
|
||||||
if (world.isClient) return;
|
|
||||||
|
|
||||||
if (player.getItemCooldownManager().isCoolingDown(this)) return;
|
|
||||||
if (!consumeAmmo(player)) return;
|
|
||||||
player.getWorld().playSound(null, player.getBlockPos(),
|
|
||||||
SoundEvents.ENTITY_GENERIC_EXPLODE, SoundCategory.PLAYERS, 0.5f, 1.8f);
|
|
||||||
BulletEntity bullet = new BulletEntity(world, player);
|
|
||||||
bullet.setVelocity(player, player.getPitch(), player.getYaw(), 0f, 4.5f, 1.0f);
|
|
||||||
world.spawnEntity(bullet);
|
|
||||||
|
|
||||||
player.getItemCooldownManager().set(this, 2); // fire rate
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean consumeAmmo(PlayerEntity player) {
|
|
||||||
if (player.getAbilities().creativeMode) return true;
|
if (player.getAbilities().creativeMode) return true;
|
||||||
|
|
||||||
for (int i = 0; i < player.getInventory().size(); i++) {
|
for (int i = 0; i < player.getInventory().size(); i++) {
|
||||||
@@ -50,7 +29,7 @@ public class AK47Item extends Item {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UseAction getUseAction(ItemStack stack) {
|
public UseAction getUseAction(ItemStack stack) {
|
||||||
return UseAction.NONE;
|
return UseAction.BOW; // raises arm
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package dev.tggamesyt.szar;
|
|||||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
|
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
|
||||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
@@ -13,14 +14,20 @@ import net.minecraft.network.PacketByteBuf;
|
|||||||
import net.minecraft.particle.ParticleTypes;
|
import net.minecraft.particle.ParticleTypes;
|
||||||
import net.minecraft.registry.RegistryKeys;
|
import net.minecraft.registry.RegistryKeys;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
import net.minecraft.util.hit.EntityHitResult;
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class BulletEntity extends ThrownItemEntity {
|
public class BulletEntity extends ThrownItemEntity {
|
||||||
|
|
||||||
|
private static final float BASE_DAMAGE = 13.0F;
|
||||||
|
private static final float PIERCE_BREAK_THRESHOLD = 0.4F;
|
||||||
|
|
||||||
|
private float pierceValue = 1.0F;
|
||||||
private int stillTicks = 0;
|
private int stillTicks = 0;
|
||||||
private double lastX, lastY, lastZ;
|
private double lastX, lastY, lastZ;
|
||||||
|
|
||||||
@@ -47,7 +54,7 @@ public class BulletEntity extends ThrownItemEntity {
|
|||||||
|
|
||||||
if (movedSq < 0.0001) {
|
if (movedSq < 0.0001) {
|
||||||
stillTicks++;
|
stillTicks++;
|
||||||
if (stillTicks >= 3) { // discard after 3 ticks of no movement
|
if (stillTicks >= 3) {
|
||||||
discard();
|
discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -79,20 +86,60 @@ public class BulletEntity extends ThrownItemEntity {
|
|||||||
this,
|
this,
|
||||||
livingOwner
|
livingOwner
|
||||||
);
|
);
|
||||||
target.damage(source, 13.0F);
|
// Damage scaled by remaining pierce value
|
||||||
|
target.damage(source, BASE_DAMAGE * pierceValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't discard — bullet continues through entities
|
||||||
|
// But reduce pierce value a bit for entity hits
|
||||||
|
pierceValue -= 0.3F;
|
||||||
|
if (pierceValue <= 0) {
|
||||||
discard();
|
discard();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBlockHit(net.minecraft.util.hit.BlockHitResult hit) {
|
protected void onBlockHit(BlockHitResult hit) {
|
||||||
super.onBlockHit(hit);
|
if (getWorld().isClient) return;
|
||||||
// Use exact hit position + nudge along face normal to sit on surface
|
|
||||||
|
BlockPos blockPos = hit.getBlockPos();
|
||||||
|
BlockState state = getWorld().getBlockState(blockPos);
|
||||||
Vec3d pos = hit.getPos();
|
Vec3d pos = hit.getPos();
|
||||||
Direction face = hit.getSide();
|
Direction face = hit.getSide();
|
||||||
|
|
||||||
|
float resistance = state.getBlock().getBlastResistance();
|
||||||
|
|
||||||
|
if (!state.isAir()) {
|
||||||
|
pierceValue -= resistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pierceValue <= 0) {
|
||||||
|
// Bullet stopped — spawn impact and discard
|
||||||
spawnImpact(pos, face);
|
spawnImpact(pos, face);
|
||||||
discard();
|
discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resistance < PIERCE_BREAK_THRESHOLD && !state.isAir()) {
|
||||||
|
// Break the block
|
||||||
|
if (getWorld() instanceof ServerWorld serverWorld) {
|
||||||
|
// Play break sound
|
||||||
|
getWorld().playSound(
|
||||||
|
null,
|
||||||
|
blockPos,
|
||||||
|
state.getSoundGroup().getBreakSound(),
|
||||||
|
SoundCategory.BLOCKS,
|
||||||
|
1.0F,
|
||||||
|
1.0F
|
||||||
|
);
|
||||||
|
serverWorld.breakBlock(blockPos, true, getOwner());
|
||||||
|
}
|
||||||
|
// Bullet continues — don't call super, don't discard
|
||||||
|
} else {
|
||||||
|
// Block too strong to break — bullet stops here
|
||||||
|
spawnImpact(pos, face);
|
||||||
|
discard();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnImpact(Vec3d pos, Direction face) {
|
private void spawnImpact(Vec3d pos, Direction face) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package dev.tggamesyt.szar;
|
|||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.SpawnReason;
|
||||||
import net.minecraft.entity.ai.goal.Goal;
|
import net.minecraft.entity.ai.goal.Goal;
|
||||||
import net.minecraft.entity.ai.goal.LookAroundGoal;
|
import net.minecraft.entity.ai.goal.LookAroundGoal;
|
||||||
import net.minecraft.entity.ai.goal.MeleeAttackGoal;
|
import net.minecraft.entity.ai.goal.MeleeAttackGoal;
|
||||||
@@ -13,10 +14,21 @@ import net.minecraft.entity.damage.DamageSource;
|
|||||||
import net.minecraft.entity.mob.MobEntity;
|
import net.minecraft.entity.mob.MobEntity;
|
||||||
import net.minecraft.entity.mob.PathAwareEntity;
|
import net.minecraft.entity.mob.PathAwareEntity;
|
||||||
import net.minecraft.entity.passive.VillagerEntity;
|
import net.minecraft.entity.passive.VillagerEntity;
|
||||||
|
import net.minecraft.registry.RegistryKey;
|
||||||
|
import net.minecraft.registry.RegistryKeys;
|
||||||
|
import net.minecraft.registry.entry.RegistryEntry;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.structure.StructureStart;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import net.minecraft.world.ServerWorldAccess;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.gen.structure.Structure;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class EpsteinEntity extends PathAwareEntity implements Arrestable {
|
public class EpsteinEntity extends PathAwareEntity implements Arrestable {
|
||||||
|
|
||||||
@@ -24,6 +36,7 @@ public class EpsteinEntity extends PathAwareEntity implements Arrestable {
|
|||||||
|
|
||||||
public EpsteinEntity(EntityType<? extends PathAwareEntity> type, World world) {
|
public EpsteinEntity(EntityType<? extends PathAwareEntity> type, World world) {
|
||||||
super(type, world);
|
super(type, world);
|
||||||
|
this.setPersistent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ import net.minecraft.entity.mob.MobEntity;
|
|||||||
import net.minecraft.entity.mob.PathAwareEntity;
|
import net.minecraft.entity.mob.PathAwareEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.item.Items;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtList;
|
||||||
|
import net.minecraft.nbt.NbtString;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
@@ -241,6 +244,15 @@ public class IslamTerrorist extends PathAwareEntity implements Arrestable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dropLoot(DamageSource source, boolean causedByPlayer) {
|
||||||
|
Random r = new Random();
|
||||||
|
int number = r.nextInt(3) + 1;
|
||||||
|
ItemStack powder = new ItemStack(Items.GUNPOWDER, number);
|
||||||
|
|
||||||
|
this.dropStack(powder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ================= DAMAGE =================
|
// ================= DAMAGE =================
|
||||||
|
|
||||||
|
|||||||
57
src/main/java/dev/tggamesyt/szar/IslandStructure.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package dev.tggamesyt.szar;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import net.minecraft.structure.StructurePlacementData;
|
||||||
|
import net.minecraft.util.BlockRotation;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.ChunkPos;
|
||||||
|
import net.minecraft.world.Heightmap;
|
||||||
|
import net.minecraft.world.gen.structure.Structure;
|
||||||
|
import net.minecraft.world.gen.structure.StructureType;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class IslandStructure extends Structure {
|
||||||
|
|
||||||
|
public static final Codec<IslandStructure> CODEC =
|
||||||
|
Structure.createCodec(IslandStructure::new);
|
||||||
|
|
||||||
|
public IslandStructure(Config config) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<StructurePosition> getStructurePosition(Context context) {
|
||||||
|
ChunkPos chunkPos = context.chunkPos();
|
||||||
|
int x = chunkPos.getCenterX();
|
||||||
|
int z = chunkPos.getCenterZ();
|
||||||
|
|
||||||
|
// Find water surface — scan down from world height to sea level
|
||||||
|
int seaLevel = context.chunkGenerator().getSeaLevel();
|
||||||
|
|
||||||
|
int surfaceY = context.chunkGenerator().getHeightInGround(
|
||||||
|
x, z,
|
||||||
|
Heightmap.Type.OCEAN_FLOOR_WG,
|
||||||
|
context.world(),
|
||||||
|
context.noiseConfig()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Must be underwater (ocean floor below sea level)
|
||||||
|
if (surfaceY >= seaLevel - 2) return Optional.empty();
|
||||||
|
|
||||||
|
// Place structure at sea level + 1 so it sits on the water surface
|
||||||
|
BlockPos pos = new BlockPos(x, seaLevel + 1, z);
|
||||||
|
|
||||||
|
StructurePlacementData placement = new StructurePlacementData()
|
||||||
|
.setRotation(BlockRotation.random(context.random()));
|
||||||
|
|
||||||
|
return Structure.getStructurePosition(context, Heightmap.Type.WORLD_SURFACE_WG, collector ->
|
||||||
|
collector.addPiece(new IslandStructurePiece(context, pos, BlockPos.ORIGIN, placement))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructureType<?> getType() {
|
||||||
|
return Szar.ISLAND_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/main/java/dev/tggamesyt/szar/IslandStructurePiece.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package dev.tggamesyt.szar;
|
||||||
|
|
||||||
|
import net.minecraft.block.entity.ChestBlockEntity;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.structure.SimpleStructurePiece;
|
||||||
|
import net.minecraft.structure.StructureContext;
|
||||||
|
import net.minecraft.structure.StructurePlacementData;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockBox;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.ChunkPos;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import net.minecraft.world.ServerWorldAccess;
|
||||||
|
import net.minecraft.world.StructureWorldAccess;
|
||||||
|
import net.minecraft.world.gen.StructureAccessor;
|
||||||
|
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||||
|
import net.minecraft.world.gen.structure.Structure;
|
||||||
|
|
||||||
|
public class IslandStructurePiece extends SimpleStructurePiece {
|
||||||
|
|
||||||
|
private static final Identifier TEMPLATE_ID =
|
||||||
|
new Identifier(Szar.MOD_ID, "island");
|
||||||
|
|
||||||
|
/* ===== NORMAL CONSTRUCTOR (Worldgen) ===== */
|
||||||
|
public IslandStructurePiece(
|
||||||
|
Structure.Context context,
|
||||||
|
BlockPos pos,
|
||||||
|
BlockPos origin,
|
||||||
|
StructurePlacementData placement
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
Szar.ISLAND_PIECE,
|
||||||
|
0,
|
||||||
|
context.structureTemplateManager(),
|
||||||
|
TEMPLATE_ID,
|
||||||
|
TEMPLATE_ID.toString(),
|
||||||
|
placement,
|
||||||
|
pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== NBT CONSTRUCTOR (Chunk Save/Load) ===== */
|
||||||
|
public IslandStructurePiece(StructureContext context, NbtCompound nbt) {
|
||||||
|
super(
|
||||||
|
Szar.ISLAND_PIECE,
|
||||||
|
nbt,
|
||||||
|
context.structureTemplateManager(),
|
||||||
|
identifier -> new StructurePlacementData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Metadata Handler (DATA structure blocks) ===== */
|
||||||
|
@Override
|
||||||
|
protected void handleMetadata(
|
||||||
|
String metadata,
|
||||||
|
BlockPos pos,
|
||||||
|
ServerWorldAccess world,
|
||||||
|
Random random,
|
||||||
|
BlockBox boundingBox
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate(StructureWorldAccess world, StructureAccessor structureAccessor,
|
||||||
|
ChunkGenerator chunkGenerator, Random random,
|
||||||
|
BlockBox chunkBox, ChunkPos chunkPos, BlockPos pivot) {
|
||||||
|
|
||||||
|
// This actually places the structure blocks
|
||||||
|
super.generate(world, structureAccessor, chunkGenerator, random, chunkBox, chunkPos, pivot);
|
||||||
|
|
||||||
|
BlockBox box = this.getBoundingBox();
|
||||||
|
for (int bx = box.getMinX(); bx <= box.getMaxX(); bx++) {
|
||||||
|
for (int by = box.getMinY(); by <= box.getMaxY(); by++) {
|
||||||
|
for (int bz = box.getMinZ(); bz <= box.getMaxZ(); bz++) {
|
||||||
|
BlockPos pos = new BlockPos(bx, by, bz);
|
||||||
|
if (world.getBlockEntity(pos) instanceof ChestBlockEntity chest) {
|
||||||
|
chest.setLootTable(
|
||||||
|
new Identifier(Szar.MOD_ID, "chests/island"),
|
||||||
|
random.nextLong()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package dev.tggamesyt.szar;
|
package dev.tggamesyt.szar;
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
@@ -15,6 +17,8 @@ import net.minecraft.util.UseAction;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import static dev.tggamesyt.szar.Szar.REVOLVER_STATE_SYNC;
|
||||||
|
|
||||||
public class RevolverItem extends Item {
|
public class RevolverItem extends Item {
|
||||||
|
|
||||||
public static final int CHAMBERS = 6;
|
public static final int CHAMBERS = 6;
|
||||||
@@ -75,4 +79,16 @@ public class RevolverItem extends Item {
|
|||||||
return 72000; // held indefinitely
|
return 72000; // held indefinitely
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void syncRevolverToClient(ServerPlayerEntity player, ItemStack stack) {
|
||||||
|
boolean[] chambers = RevolverItem.getChambers(stack);
|
||||||
|
int current = RevolverItem.getCurrentChamber(stack);
|
||||||
|
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
for (int i = 0; i < RevolverItem.CHAMBERS; i++) {
|
||||||
|
buf.writeBoolean(chambers[i]);
|
||||||
|
}
|
||||||
|
buf.writeInt(current);
|
||||||
|
ServerPlayNetworking.send(player, REVOLVER_STATE_SYNC, buf);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,10 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
|||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||||
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
||||||
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||||
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
||||||
|
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
|
||||||
import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
|
import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
|
||||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||||
@@ -26,6 +28,7 @@ import net.minecraft.advancement.Advancement;
|
|||||||
import net.minecraft.block.*;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.block.entity.ChestBlockEntity;
|
||||||
import net.minecraft.entity.*;
|
import net.minecraft.entity.*;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
import net.minecraft.entity.damage.DamageType;
|
import net.minecraft.entity.damage.DamageType;
|
||||||
@@ -38,6 +41,11 @@ import net.minecraft.entity.effect.StatusEffectInstance;
|
|||||||
import net.minecraft.entity.passive.VillagerEntity;
|
import net.minecraft.entity.passive.VillagerEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.*;
|
import net.minecraft.item.*;
|
||||||
|
import net.minecraft.loot.LootPool;
|
||||||
|
import net.minecraft.loot.entry.ItemEntry;
|
||||||
|
import net.minecraft.loot.function.SetCountLootFunction;
|
||||||
|
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.registry.*;
|
import net.minecraft.registry.*;
|
||||||
import net.minecraft.registry.entry.RegistryEntry;
|
import net.minecraft.registry.entry.RegistryEntry;
|
||||||
@@ -79,6 +87,7 @@ import net.minecraft.world.gen.structure.StructureType;
|
|||||||
import net.minecraft.world.poi.PointOfInterestType;
|
import net.minecraft.world.poi.PointOfInterestType;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.core.jmx.Server;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -93,9 +102,16 @@ public class Szar implements ModInitializer {
|
|||||||
public static MinecraftServer SERVER;
|
public static MinecraftServer SERVER;
|
||||||
public static final Identifier REVOLVER_SHOOT = new Identifier(MOD_ID, "revolver_shoot");
|
public static final Identifier REVOLVER_SHOOT = new Identifier(MOD_ID, "revolver_shoot");
|
||||||
public static final Identifier REVOLVER_SPIN = new Identifier(MOD_ID, "revolver_spin");
|
public static final Identifier REVOLVER_SPIN = new Identifier(MOD_ID, "revolver_spin");
|
||||||
public static final Identifier REVOLVER_SYNC = new Identifier(MOD_ID, "revolver_sync");
|
public static final Identifier REVOLVER_STATE_SYNC = new Identifier(MOD_ID, "revolver_state_sync");
|
||||||
public static final Identifier BULLET_IMPACT = new Identifier(MOD_ID, "bullet_impact");
|
public static final Identifier BULLET_IMPACT = new Identifier(MOD_ID, "bullet_impact");
|
||||||
|
public static final Identifier REVOLVER_SCROLL = new Identifier(MOD_ID, "revolver_scroll");
|
||||||
|
public static final Identifier REVOLVER_SPIN_RESULT = new Identifier(MOD_ID, "revolver_spin_result"); // S2C
|
||||||
|
public static final Identifier AK47_SHOOT = new Identifier(MOD_ID, "ak47_shoot");
|
||||||
public static final Identifier REVOLVER_CHAMBER_CHANGE = new Identifier(MOD_ID, "revolver_chamber_change");
|
public static final Identifier REVOLVER_CHAMBER_CHANGE = new Identifier(MOD_ID, "revolver_chamber_change");
|
||||||
|
public static final SoundEvent REVOLVER_CLICK1_SOUND = SoundEvent.of(new Identifier(MOD_ID, "revolver_click1"));
|
||||||
|
public static final SoundEvent REVOLVER_CLICK2_SOUND = SoundEvent.of(new Identifier(MOD_ID, "revolver_click2"));
|
||||||
|
public static final SoundEvent REVOLVER_CLICK3_SOUND = SoundEvent.of(new Identifier(MOD_ID, "revolver_click3"));
|
||||||
|
public static final SoundEvent REVOLVER_ROLL_SOUND = SoundEvent.of(new Identifier(MOD_ID, "revolver_roll"));
|
||||||
public static final SoundEvent BESZIV = Registry.register(
|
public static final SoundEvent BESZIV = Registry.register(
|
||||||
Registries.SOUND_EVENT,
|
Registries.SOUND_EVENT,
|
||||||
new Identifier(MOD_ID, "besziv"),
|
new Identifier(MOD_ID, "besziv"),
|
||||||
@@ -239,6 +255,7 @@ public class Szar implements ModInitializer {
|
|||||||
.dimensions(EntityDimensions.fixed(0.6F, 1.8F)) // player-sized
|
.dimensions(EntityDimensions.fixed(0.6F, 1.8F)) // player-sized
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
public static final EntityType<HitterEntity> HitterEntityType =
|
public static final EntityType<HitterEntity> HitterEntityType =
|
||||||
Registry.register(
|
Registry.register(
|
||||||
Registries.ENTITY_TYPE,
|
Registries.ENTITY_TYPE,
|
||||||
@@ -373,12 +390,62 @@ public class Szar implements ModInitializer {
|
|||||||
private final Map<UUID, BlockPos> sleepingPlayers = new HashMap<>();
|
private final Map<UUID, BlockPos> sleepingPlayers = new HashMap<>();
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(AK47_SHOOT, (server, player, handler, buf, responseSender) -> {
|
||||||
|
server.execute(() -> {
|
||||||
|
ItemStack stack = player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.AK47)) return;
|
||||||
|
if (player.getItemCooldownManager().isCoolingDown(Szar.AK47)) return;
|
||||||
|
|
||||||
|
AK47Item ak = (AK47Item) Szar.AK47;
|
||||||
|
if (!ak.consumeAmmo(player)) return;
|
||||||
|
|
||||||
|
player.getWorld().playSound(null, player.getBlockPos(),
|
||||||
|
SoundEvents.ENTITY_GENERIC_EXPLODE, SoundCategory.PLAYERS, 0.5f, 1.8f);
|
||||||
|
|
||||||
|
BulletEntity bullet = new BulletEntity(player.getWorld(), player);
|
||||||
|
bullet.setVelocity(player, player.getPitch(), player.getYaw(), 0f, 4.5f, 1.0f);
|
||||||
|
player.getWorld().spawnEntity(bullet);
|
||||||
|
// Recoil when shooting downward while falling
|
||||||
|
recoil(player, 0.1);
|
||||||
|
player.getItemCooldownManager().set(Szar.AK47, 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
||||||
PoliceSpawnTimerStore.remove(handler.player);
|
PoliceSpawnTimerStore.remove(handler.player);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_SCROLL, (server, player, handler, buf, responseSender) -> {
|
||||||
|
int direction = buf.readInt();
|
||||||
|
server.execute(() -> {
|
||||||
|
ItemStack stack = player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.REVOLVER)) return;
|
||||||
|
int current = RevolverItem.getCurrentChamber(stack);
|
||||||
|
int next = (current + direction + RevolverItem.CHAMBERS) % RevolverItem.CHAMBERS;
|
||||||
|
RevolverItem.setCurrentChamber(stack, next);
|
||||||
|
playRevolverClick(player);
|
||||||
|
RevolverItem.syncRevolverToClient(player, stack); // <- here
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_SPIN, (server, player, handler, buf, responseSender) -> {
|
||||||
|
server.execute(() -> {
|
||||||
|
ItemStack stack = player.getMainHandStack();
|
||||||
|
if (!stack.isOf(Szar.REVOLVER)) return;
|
||||||
|
int steps = 1 + player.getWorld().getRandom().nextInt(RevolverItem.CHAMBERS);
|
||||||
|
int current = RevolverItem.getCurrentChamber(stack);
|
||||||
|
RevolverItem.setCurrentChamber(stack, (current + steps) % RevolverItem.CHAMBERS);
|
||||||
|
player.getWorld().playSound(null, player.getBlockPos(),
|
||||||
|
Szar.REVOLVER_ROLL_SOUND, SoundCategory.PLAYERS, 1f, 1f);
|
||||||
|
|
||||||
|
// Tell client how many steps for animation
|
||||||
|
PacketByteBuf replyBuf = PacketByteBufs.create();
|
||||||
|
replyBuf.writeInt(steps);
|
||||||
|
ServerPlayNetworking.send(player, REVOLVER_SPIN_RESULT, replyBuf);
|
||||||
|
RevolverItem.syncRevolverToClient(player, stack);
|
||||||
|
});
|
||||||
|
});
|
||||||
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_CHAMBER_CHANGE, (server, player, handler, buf, responseSender) -> {
|
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_CHAMBER_CHANGE, (server, player, handler, buf, responseSender) -> {
|
||||||
int index = buf.readInt();
|
int index = buf.readInt();
|
||||||
boolean wasLoaded = buf.readBoolean(); // true = unloading, false = loading
|
boolean wasLoaded = buf.readBoolean();
|
||||||
|
|
||||||
server.execute(() -> {
|
server.execute(() -> {
|
||||||
ItemStack stack = player.getMainHandStack();
|
ItemStack stack = player.getMainHandStack();
|
||||||
@@ -387,12 +454,10 @@ public class Szar implements ModInitializer {
|
|||||||
boolean[] chambers = RevolverItem.getChambers(stack);
|
boolean[] chambers = RevolverItem.getChambers(stack);
|
||||||
|
|
||||||
if (wasLoaded) {
|
if (wasLoaded) {
|
||||||
// Unload — give shell
|
|
||||||
chambers[index] = false;
|
chambers[index] = false;
|
||||||
RevolverItem.setChambers(stack, chambers);
|
RevolverItem.setChambers(stack, chambers);
|
||||||
player.getInventory().insertStack(new ItemStack(Szar.BULLET_ITEM));
|
player.getInventory().insertStack(new ItemStack(Szar.BULLET_ITEM));
|
||||||
} else {
|
} else {
|
||||||
// Load — take bullet from inventory
|
|
||||||
for (int i = 0; i < player.getInventory().size(); i++) {
|
for (int i = 0; i < player.getInventory().size(); i++) {
|
||||||
ItemStack s = player.getInventory().getStack(i);
|
ItemStack s = player.getInventory().getStack(i);
|
||||||
if (!s.isEmpty() && s.isOf(Szar.BULLET_ITEM)) {
|
if (!s.isEmpty() && s.isOf(Szar.BULLET_ITEM)) {
|
||||||
@@ -403,33 +468,11 @@ public class Szar implements ModInitializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_SYNC, (server, player, handler, buf, responseSender) -> {
|
|
||||||
// Read 6 booleans from packet
|
|
||||||
boolean[] chambers = new boolean[RevolverItem.CHAMBERS];
|
|
||||||
for (int i = 0; i < RevolverItem.CHAMBERS; i++) {
|
|
||||||
chambers[i] = buf.readBoolean();
|
|
||||||
}
|
|
||||||
int currentChamber = buf.readInt();
|
|
||||||
|
|
||||||
server.execute(() -> {
|
// Advance to next chamber after loading/unloading
|
||||||
ItemStack stack = player.getMainHandStack();
|
RevolverItem.setCurrentChamber(stack, (index + 1) % RevolverItem.CHAMBERS);
|
||||||
if (!stack.isOf(Szar.REVOLVER)) return;
|
playRevolverClick(player);
|
||||||
RevolverItem.setChambers(stack, chambers);
|
RevolverItem.syncRevolverToClient(player, stack);
|
||||||
RevolverItem.setCurrentChamber(stack, currentChamber);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_SPIN, (server, player, handler, buf, responseSender) -> {
|
|
||||||
server.execute(() -> {
|
|
||||||
ItemStack stack = player.getMainHandStack();
|
|
||||||
if (!stack.isOf(Szar.REVOLVER)) return;
|
|
||||||
int steps = 1 + player.getWorld().getRandom().nextInt(RevolverItem.CHAMBERS);
|
|
||||||
int current = RevolverItem.getCurrentChamber(stack);
|
|
||||||
RevolverItem.setCurrentChamber(stack, (current + steps) % RevolverItem.CHAMBERS);
|
|
||||||
// Notify player
|
|
||||||
player.sendMessage(Text.literal("*click* chamber " +
|
|
||||||
(RevolverItem.getCurrentChamber(stack) + 1)).formatted(Formatting.GRAY), true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_SHOOT, (server, player, handler, buf, responseSender) -> {
|
ServerPlayNetworking.registerGlobalReceiver(REVOLVER_SHOOT, (server, player, handler, buf, responseSender) -> {
|
||||||
@@ -462,11 +505,15 @@ public class Szar implements ModInitializer {
|
|||||||
BulletEntity bullet = new BulletEntity(player.getWorld(), player);
|
BulletEntity bullet = new BulletEntity(player.getWorld(), player);
|
||||||
bullet.setVelocity(player, player.getPitch(), player.getYaw(), 0f, 4.5f, 0.0f);
|
bullet.setVelocity(player, player.getPitch(), player.getYaw(), 0f, 4.5f, 0.0f);
|
||||||
player.getWorld().spawnEntity(bullet);
|
player.getWorld().spawnEntity(bullet);
|
||||||
|
// Recoil when shooting downward while falling
|
||||||
|
recoil(player, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always advance chamber after trigger pull
|
// Always advance chamber after trigger pull
|
||||||
RevolverItem.setCurrentChamber(stack, (current + 1) % RevolverItem.CHAMBERS);
|
RevolverItem.setCurrentChamber(stack, (current + 1) % RevolverItem.CHAMBERS);
|
||||||
|
playRevolverClick(player);
|
||||||
|
RevolverItem.syncRevolverToClient(player, stack);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ServerPlayNetworking.registerGlobalReceiver(CONFIG_SYNC,
|
ServerPlayNetworking.registerGlobalReceiver(CONFIG_SYNC,
|
||||||
@@ -759,12 +806,6 @@ public class Szar implements ModInitializer {
|
|||||||
NyanEntityType,
|
NyanEntityType,
|
||||||
1, 1, 1
|
1, 1, 1
|
||||||
);
|
);
|
||||||
BiomeModifications.addSpawn(
|
|
||||||
BiomeSelectors.includeByKey(BiomeKeys.FOREST, BiomeKeys.FLOWER_FOREST),
|
|
||||||
SpawnGroup.MONSTER,
|
|
||||||
EpsteinEntityType,
|
|
||||||
1, 1, 1
|
|
||||||
);
|
|
||||||
BiomeModifications.addFeature(
|
BiomeModifications.addFeature(
|
||||||
BiomeSelectors.tag(BiomeTags.IS_JUNGLE),
|
BiomeSelectors.tag(BiomeTags.IS_JUNGLE),
|
||||||
GenerationStep.Feature.VEGETAL_DECORATION,
|
GenerationStep.Feature.VEGETAL_DECORATION,
|
||||||
@@ -912,6 +953,26 @@ public class Szar implements ModInitializer {
|
|||||||
new Identifier(MOD_ID, "nyansniffer"),
|
new Identifier(MOD_ID, "nyansniffer"),
|
||||||
new PaintingVariant(32, 32)
|
new PaintingVariant(32, 32)
|
||||||
);
|
);
|
||||||
|
// In Szar.java onInitialize:
|
||||||
|
UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
|
||||||
|
if (world.isClient) return ActionResult.PASS;
|
||||||
|
BlockPos pos = hitResult.getBlockPos();
|
||||||
|
if (!(world.getBlockEntity(pos) instanceof ChestBlockEntity chest)) return ActionResult.PASS;
|
||||||
|
|
||||||
|
// Check if this is an island chest by checking if it has our loot table pending
|
||||||
|
// Once loot generates we can't check anymore, so tag it via NBT during structure gen
|
||||||
|
NbtCompound nbt = chest.createNbt();
|
||||||
|
if (!nbt.getString("szar_chest_type").equals("island")) return ActionResult.PASS;
|
||||||
|
|
||||||
|
// Set center item if not already set
|
||||||
|
ItemStack center = chest.getStack(13);
|
||||||
|
if (center.isEmpty()) {
|
||||||
|
chest.setStack(13, new ItemStack(Szar.EPSTEIN_FILES));
|
||||||
|
}
|
||||||
|
nbt.remove("szar_chest_type");
|
||||||
|
|
||||||
|
return ActionResult.PASS;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// In your ModItems or wherever you register items
|
// In your ModItems or wherever you register items
|
||||||
@@ -1066,6 +1127,19 @@ public class Szar implements ModInitializer {
|
|||||||
new Identifier(MOD_ID, "casino"),
|
new Identifier(MOD_ID, "casino"),
|
||||||
() -> CasinoStructure.CODEC
|
() -> CasinoStructure.CODEC
|
||||||
);
|
);
|
||||||
|
public static final StructurePieceType ISLAND_PIECE =
|
||||||
|
Registry.register(
|
||||||
|
Registries.STRUCTURE_PIECE,
|
||||||
|
new Identifier(MOD_ID, "island_piece"),
|
||||||
|
IslandStructurePiece::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final StructureType<IslandStructure> ISLAND_TYPE =
|
||||||
|
Registry.register(
|
||||||
|
Registries.STRUCTURE_TYPE,
|
||||||
|
new Identifier(MOD_ID, "island"),
|
||||||
|
() -> IslandStructure.CODEC
|
||||||
|
);
|
||||||
static VoxelShape shape0 = VoxelShapes.cuboid(0.1875f, 0f, 0.625f, 0.6875f, 0.5f, 1.125f);
|
static VoxelShape shape0 = VoxelShapes.cuboid(0.1875f, 0f, 0.625f, 0.6875f, 0.5f, 1.125f);
|
||||||
static VoxelShape shape1 = VoxelShapes.cuboid(0.1875f, 1.5f, 0.625f, 0.6875f, 2f, 1.125f);
|
static VoxelShape shape1 = VoxelShapes.cuboid(0.1875f, 1.5f, 0.625f, 0.6875f, 2f, 1.125f);
|
||||||
static VoxelShape shape2 = VoxelShapes.cuboid(0.5625f, 0f, 0.25f, 1.0625f, 2f, 0.75f);
|
static VoxelShape shape2 = VoxelShapes.cuboid(0.5625f, 0f, 0.25f, 1.0625f, 2f, 0.75f);
|
||||||
@@ -1732,32 +1806,29 @@ public class Szar implements ModInitializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void shootBullet(ServerPlayerEntity player) {
|
public static void recoil(ServerPlayerEntity player, double recoil) {
|
||||||
World world = player.getWorld();
|
if (player.isCreative()) {
|
||||||
Vec3d start = player.getEyePos();
|
return;
|
||||||
Vec3d dir = player.getRotationVec(1.0f);
|
}
|
||||||
Vec3d end = start.add(dir.multiply(100)); // 100 block range
|
double recoilfinal = player.isOnGround() ? recoil / 2 : recoil;
|
||||||
|
|
||||||
net.minecraft.util.hit.HitResult hit = world.raycast(new net.minecraft.world.RaycastContext(
|
// Opposite of the direction the player is looking
|
||||||
start, end,
|
float pitch = (float) Math.toRadians(player.getPitch());
|
||||||
net.minecraft.world.RaycastContext.ShapeType.COLLIDER,
|
float yaw = (float) Math.toRadians(player.getYaw());
|
||||||
net.minecraft.world.RaycastContext.FluidHandling.NONE,
|
|
||||||
player
|
double dx = -(-Math.cos(pitch) * Math.sin(yaw)) * recoilfinal;
|
||||||
));
|
double dy = -(- Math.sin(pitch)) * recoilfinal;
|
||||||
|
double dz = -( Math.cos(pitch) * Math.cos(yaw)) * recoilfinal;
|
||||||
// Entity hit check
|
|
||||||
Box box = new Box(start, end).expand(1);
|
player.addVelocity(dx, dy, dz);
|
||||||
net.minecraft.util.hit.EntityHitResult entityHit =
|
player.velocityModified = true;
|
||||||
net.minecraft.entity.projectile.ProjectileUtil.raycast(
|
player.fallDistance = Math.max(0, player.fallDistance - (float)(dy * 8));
|
||||||
player, start, end, box,
|
}
|
||||||
e -> !e.isSpectator() && e != player && e.canHit(), 100 * 100
|
|
||||||
);
|
private static void playRevolverClick(ServerPlayerEntity player) {
|
||||||
|
float pitch = 0.9F + player.getWorld().getRandom().nextFloat() * 0.2F; // 0.9 to 1.1
|
||||||
if (entityHit != null) {
|
player.getWorld().playSound(null, player.getBlockPos(),
|
||||||
entityHit.getEntity().damage(
|
Szar.REVOLVER_CLICK2_SOUND, SoundCategory.PLAYERS, 1f, pitch);
|
||||||
world.getDamageSources().playerAttack(player), 8.0f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,5 +162,37 @@
|
|||||||
"stream": true
|
"stream": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"revolver_click1": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "szar:revolver_click1",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"revolver_click2": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "szar:revolver_click2",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"revolver_click3": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "szar:revolver_click3",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"revolver_roll": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "szar:revolver_roll",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/main/resources/assets/szar/sounds/revolver_click1.mp3
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_click1.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_click2.mp3
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_click2.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_click3.mp3
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_click3.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_roll.mp3
Normal file
BIN
src/main/resources/assets/szar/sounds/revolver_roll.ogg
Normal file
|
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"replace": false,
|
"replace": false,
|
||||||
"values": [
|
"values": [
|
||||||
"szar:niggerite_block"
|
"szar:niggerite_block",
|
||||||
|
"szar:cigany"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
61
src/main/resources/data/szar/loot_tables/chests/island.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"type": "minecraft:chest",
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"rolls": 1,
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "szar:epstein_files"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rolls": 26,
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "minecraft:gold_ingot",
|
||||||
|
"weight": 40,
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"function": "minecraft:set_count",
|
||||||
|
"count": {
|
||||||
|
"type": "minecraft:binomial",
|
||||||
|
"n": 1,
|
||||||
|
"p": 0.2
|
||||||
|
},
|
||||||
|
"add": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "minecraft:diamond",
|
||||||
|
"weight": 30,
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"function": "minecraft:set_count",
|
||||||
|
"count": {
|
||||||
|
"type": "minecraft:binomial",
|
||||||
|
"n": 1,
|
||||||
|
"p": 0.2
|
||||||
|
},
|
||||||
|
"add": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "minecraft:gold_block",
|
||||||
|
"weight": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "minecraft:diamond_block",
|
||||||
|
"weight": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
src/main/resources/data/szar/recipes/debug.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"type": "minecraft:crafting_shaped",
|
||||||
|
"pattern": [
|
||||||
|
"DDD",
|
||||||
|
"DDD",
|
||||||
|
"DDD"
|
||||||
|
],
|
||||||
|
"key": {
|
||||||
|
"D": {
|
||||||
|
"item": "minecraft:debug_stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"item": "szar:cigany",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/main/resources/data/szar/structures/island.nbt
Normal file
11
src/main/resources/data/szar/tags/worldgen/biome/island.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"minecraft:ocean",
|
||||||
|
"minecraft:deep_ocean",
|
||||||
|
"minecraft:cold_ocean",
|
||||||
|
"minecraft:deep_cold_ocean",
|
||||||
|
"minecraft:lukewarm_ocean",
|
||||||
|
"minecraft:deep_lukewarm_ocean",
|
||||||
|
"minecraft:warm_ocean"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"type": "szar:island",
|
||||||
|
"biomes": "#szar:island",
|
||||||
|
"step": "surface_structures",
|
||||||
|
"terrain_adaptation": "beard_thin",
|
||||||
|
"spawn_overrides": {},
|
||||||
|
"config": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"structures": [
|
||||||
|
{
|
||||||
|
"structure": "szar:island",
|
||||||
|
"weight": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"placement": {
|
||||||
|
"type": "minecraft:random_spread",
|
||||||
|
"spacing": 40,
|
||||||
|
"separation": 20,
|
||||||
|
"salt": 533693546
|
||||||
|
}
|
||||||
|
}
|
||||||