From ae6d9095e3a6e914c9f5a0be24be4c3ce3151007 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Tue, 16 Jan 2024 22:27:14 +0500 Subject: [PATCH] Port mod from Fabric --- build.gradle | 2 +- common/build.gradle | 2 + .../knockdowns/common/KnockdownsCommon.java | 9 ++ .../knockdowns/common/KnockdownsNetwork.java | 90 ++++++++++++ .../knockdowns/common/api/IKnockableDown.java | 15 ++ .../common/events/KnockdownsClientEvents.java | 105 +++++++++++++ .../common/events/KnockdownsEvents.java | 87 +++++++++++ .../common/mixin/PlayerEntityMixin.java | 71 +++++++++ .../mixin/client/ClientPlayerEntityMixin.java | 16 ++ .../common/packets/KnockdownsPacket.java | 19 +++ .../packets/KnockedDownStatusPacket.java | 69 +++++++++ .../PlayKnockedDownSoundS2CPacket.java | 40 +++++ .../common/packets/ReviveStatusPacket.java | 138 ++++++++++++++++++ .../registries/KnockdownsSoundEvents.java | 18 +++ .../registries/KnockedDownSoundInstance.java | 31 ++++ .../assets/knockdowns/lang/en_us.json | 101 ++++++++++++- .../assets/knockdowns/lang/ru_ru.json | 102 +++++++++++++ .../resources/assets/knockdowns/sounds.json | 10 ++ .../assets/knockdowns/sounds/knocked_down.ogg | Bin 0 -> 26287 bytes .../resources/knockdowns-common.mixins.json | 5 +- .../main/resources/knockdowns.accesswidener | 5 +- .../src/main/resources/knockdowns.mixins.json | 2 +- forge/build.gradle | 2 + .../knockdowns/forge/KnockdownsForge.java | 2 +- .../src/main/resources/knockdowns.mixins.json | 2 +- gradle.properties | 2 +- 26 files changed, 936 insertions(+), 9 deletions(-) create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java create mode 100644 common/src/main/resources/assets/knockdowns/lang/ru_ru.json create mode 100644 common/src/main/resources/assets/knockdowns/sounds.json create mode 100644 common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg diff --git a/build.gradle b/build.gradle index d5234af..b4c6a6d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.3-SNAPSHOT" apply false + id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false } architectury { diff --git a/common/build.gradle b/common/build.gradle index fe38fa0..a545d0d 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -12,6 +12,8 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" // Remove the next line if you don't want to depend on the API modApi "dev.architectury:architectury:${rootProject.architectury_version}" + + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.3")) } publishing { diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java index f41886f..d7fea13 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java @@ -1,7 +1,16 @@ package ru.octol1ttle.knockdowns.common; +import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents; +import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents; +import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; + public class KnockdownsCommon { public static final String MOD_ID = "knockdowns"; + public static void init() { + KnockdownsSoundEvents.register(); + KnockdownsNetwork.registerPackets(); + KnockdownsClientEvents.registerCallbacks(); + KnockdownsEvents.registerCallbacks(); } } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java new file mode 100644 index 0000000..3e552bc --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java @@ -0,0 +1,90 @@ +package ru.octol1ttle.knockdowns.common; + +import dev.architectury.networking.NetworkChannel; +import dev.architectury.networking.NetworkManager; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.Packet; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.EntityTrackingListener; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import net.minecraft.util.Identifier; +import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.packets.PlayKnockedDownSoundS2CPacket; +import ru.octol1ttle.knockdowns.common.packets.ReviveStatusPacket; + +public class KnockdownsNetwork { + private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main")); + public static void registerPackets() { + CHANNEL.register(KnockedDownStatusPacket.SendS2C.class, KnockedDownStatusPacket.SendS2C::encode, KnockedDownStatusPacket.SendS2C::new, KnockedDownStatusPacket.SendS2C::apply); + CHANNEL.register(KnockedDownStatusPacket.RequestC2S.class, KnockedDownStatusPacket.RequestC2S::encode, KnockedDownStatusPacket.RequestC2S::new, KnockedDownStatusPacket.RequestC2S::apply); + + CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply); + + CHANNEL.register(ReviveStatusPacket.SendS2C.class, ReviveStatusPacket.SendS2C::encode, ReviveStatusPacket.SendS2C::new, ReviveStatusPacket.SendS2C::apply); + CHANNEL.register(ReviveStatusPacket.SendC2S.class, ReviveStatusPacket.SendC2S::encode, ReviveStatusPacket.SendC2S::new, ReviveStatusPacket.SendC2S::apply); + CHANNEL.register(ReviveStatusPacket.RequestC2S.class, ReviveStatusPacket.RequestC2S::encode, ReviveStatusPacket.RequestC2S::new, ReviveStatusPacket.RequestC2S::apply); + CHANNEL.register(ReviveStatusPacket.RevivedC2S.class, ReviveStatusPacket.RevivedC2S::encode, ReviveStatusPacket.RevivedC2S::new, ReviveStatusPacket.RevivedC2S::apply); + } + + public static void sendToServer(T message) { + if (CHANNEL.canServerReceive(message.getClass())) { + CHANNEL.sendToServer(message); + } + } + + public static void sendToPlayer(PlayerEntity player, T message) { + Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); + Class messageClass = message.getClass(); + + sendToPlayer(player, packet, messageClass); + } + + public static void sendToPlayer(PlayerEntity player, Packet packet, Class messageClass) { + if (!(player instanceof ServerPlayerEntity serverPlayer)) { + throw new IllegalArgumentException("Cannot send to client players"); + } + if (CHANNEL.canPlayerReceive(serverPlayer, messageClass)) { + serverPlayer.networkHandler.sendPacket(packet); + } + } + + // TODO: PR to Architectury API + public static void sendToListeners(Entity entity, T message) { + Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); + Class messageClass = message.getClass(); + + sendToListeners(entity, packet, messageClass); + } + + private static void sendToListeners(Entity entity, Packet packet, Class messageClass) { + ServerChunkManager chunkManager = (ServerChunkManager) entity.getWorld().getChunkManager(); + ThreadedAnvilChunkStorage.EntityTracker entityTracker = chunkManager.threadedAnvilChunkStorage.entityTrackers.get(entity.getId()); + + for (EntityTrackingListener listener : entityTracker.listeners) { + sendToPlayer(listener.getPlayer(), packet, messageClass); + } + } + + public static void sendToListenersAndSelf(PlayerEntity player, T message) { + Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); + Class messageClass = message.getClass(); + sendToPlayer(player, packet, messageClass); + sendToListeners(player, packet, messageClass); + } + + public static void sendToWorld(ServerWorld world, T message) { + Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); + Class messageClass = message.getClass(); + + sendToWorld(world, packet, messageClass); + } + + private static void sendToWorld(ServerWorld world, Packet packet, Class messageClass) { + for (ServerPlayerEntity player : world.getPlayers()) { + sendToPlayer(player, packet, messageClass); + } + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java new file mode 100644 index 0000000..03f010f --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java @@ -0,0 +1,15 @@ +package ru.octol1ttle.knockdowns.common.api; + +import java.util.UUID; + +public interface IKnockableDown { + boolean knockdowns$isKnockedDown(); + + void knockdowns$setKnockedDown(boolean knockedDown); + + boolean knockdowns$isBeingRevived(); + + void knockdowns$setBeingRevived(boolean beingRevived); + + UUID knockdowns$getUuid(); +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java new file mode 100644 index 0000000..459d763 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java @@ -0,0 +1,105 @@ +package ru.octol1ttle.knockdowns.common.events; + +import dev.architectury.event.EventResult; +import dev.architectury.event.events.client.ClientGuiEvent; +import dev.architectury.event.events.client.ClientPlayerEvent; +import dev.architectury.event.events.client.ClientTickEvent; +import dev.architectury.event.events.common.InteractionEvent; +import java.util.UUID; +import net.minecraft.SharedConstants; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.util.hit.HitResult; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; +import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.packets.ReviveStatusPacket; + +public class KnockdownsClientEvents { + private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND; + private static IKnockableDown reviving = null; + private static int revivalTimer = -1; + + public static void registerCallbacks() { + registerOnEntityLoad(); + registerOnEntityUse(); + registerOnWorldTick(); + registerOnHudRender(); + } + + private static void registerOnEntityLoad() { + ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> { + UUID playerUuid = player.getUuid(); + KnockdownsNetwork.sendToServer(new KnockedDownStatusPacket.RequestC2S(playerUuid)); + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RequestC2S(playerUuid)); + }); + } + + private static void registerOnEntityUse() { + InteractionEvent.INTERACT_ENTITY.register((player, entity, hand) -> { + if (!(entity instanceof IKnockableDown knockableEntity) || !knockableEntity.knockdowns$isKnockedDown() + || knockableEntity.knockdowns$isBeingRevived()) { + return EventResult.pass(); + } + + IKnockableDown self = (IKnockableDown) player; + if (self.knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + + knockableEntity.knockdowns$setBeingRevived(true); + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(entity.getUuid(), true)); + + reviving = knockableEntity; + revivalTimer = REVIVAL_WAIT_TIME; + + return EventResult.interruptTrue(); + }); + } + + private static void registerOnWorldTick() { + ClientTickEvent.ClientLevel.CLIENT_LEVEL_POST.register(world -> { + boolean revived = false; + revivalTimer--; + if (revivalTimer <= 0) { + revivalTimer = -1; + revived = true; + } + + if (reviving == null) { + return; + } + + HitResult crosshairTarget = MinecraftClient.getInstance().crosshairTarget; + if (revived || crosshairTarget == null || crosshairTarget.getType() != HitResult.Type.ENTITY + || !((EntityHitResult) crosshairTarget).getEntity().getUuid().equals(reviving.knockdowns$getUuid())) { + reviving.knockdowns$setBeingRevived(false); + + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(reviving.knockdowns$getUuid(), false)); + if (revived) { + reviving.knockdowns$setKnockedDown(false); + + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RevivedC2S(reviving.knockdowns$getUuid())); + } + + reviving = null; + revivalTimer = -1; + } + }); + } + + private static void registerOnHudRender() { + ClientGuiEvent.RENDER_HUD.register((drawContext, tickDelta) -> { + if (revivalTimer == -1) { + return; + } + + TextRenderer renderer = MinecraftClient.getInstance().textRenderer; + String text = String.format("%.1f", revivalTimer / (float) SharedConstants.TICKS_PER_SECOND); + int x = (drawContext.getScaledWindowWidth() - renderer.getWidth(text)) / 2; + + drawContext.drawTextWithShadow(renderer, text, x, drawContext.getScaledWindowHeight() / 2 + 15, 0xFFFFFF); + }); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java new file mode 100644 index 0000000..a812469 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -0,0 +1,87 @@ +package ru.octol1ttle.knockdowns.common.events; + +import dev.architectury.event.CompoundEventResult; +import dev.architectury.event.EventResult; +import dev.architectury.event.events.common.EntityEvent; +import dev.architectury.event.events.common.InteractionEvent; +import dev.architectury.event.events.common.PlayerEvent; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; +import net.minecraft.util.Hand; +import net.minecraft.world.GameRules; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; +import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.packets.PlayKnockedDownSoundS2CPacket; + +public class KnockdownsEvents { + public static void registerCallbacks() { + registerOnLivingDeath(); + registerOnPlayerInteractions(); + } + + private static void registerOnLivingDeath() { + EntityEvent.LIVING_DEATH.register((entity, source) -> { + if (!(entity instanceof IKnockableDown knockableDown) || knockableDown.knockdowns$isKnockedDown()) { + return EventResult.pass(); + } + + ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; + // TODO: timer + if (!serverPlayer.getWorld().getGameRules().getBoolean(GameRules.KEEP_INVENTORY)) { + serverPlayer.getInventory().dropAll(); + } + entity.setHealth(1.0f); + entity.setInvulnerable(true); + entity.setGlowing(true); + entity.setAir(entity.getMaxAir()); + entity.extinguish(); + entity.setFrozenTicks(0); + entity.setOnFire(false); + entity.clearStatusEffects(); + + knockableDown.knockdowns$setKnockedDown(true); + + KnockdownsNetwork.sendToListenersAndSelf(serverPlayer, new KnockedDownStatusPacket.SendS2C(serverPlayer.getUuid(), true)); + KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ())); + + TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); + Text replaced = Text.translatableWithFallback(content.getKey().replace("death.", "knockdown."), content.getKey(), content.getArgs()); + MinecraftServer server = serverPlayer.getServer(); + if (server != null) { + server.getPlayerManager().broadcast(replaced, false); + } + + return EventResult.interruptFalse(); + }); + } + + private static void registerOnPlayerInteractions() { + InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + return EventResult.pass(); + }); + PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + return EventResult.pass(); + }); + InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return CompoundEventResult.interruptFalse(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack()); + } + return CompoundEventResult.pass(); + }); + InteractionEvent.RIGHT_CLICK_BLOCK.register((player, hand, pos, direction) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + return EventResult.pass(); + }); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java new file mode 100644 index 0000000..15ebee3 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java @@ -0,0 +1,71 @@ +package ru.octol1ttle.knockdowns.common.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import java.util.UUID; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; + +@Mixin(PlayerEntity.class) +public abstract class PlayerEntityMixin implements IKnockableDown { + @Unique + private boolean knockedDown; + @Unique + private boolean beingRevived; + + @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) + private boolean enterSwimmingIfKnockedDown(boolean original) { + PlayerEntity player = (PlayerEntity)(Object)this; + if (!(player instanceof IKnockableDown knockableDown)) { + throw new IllegalStateException(); + } + + return original || knockableDown.knockdowns$isKnockedDown(); + } + + @ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN")) + private boolean dontHealIfKnockedDown(boolean original) { + return original && !this.knockdowns$isKnockedDown(); + } + + @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) + public void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { + this.knockedDown = nbt.getBoolean("KnockedDown"); + } + + @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) + public void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { + nbt.putBoolean("KnockedDown", this.knockedDown); + } + + @Override + public boolean knockdowns$isKnockedDown() { + return knockedDown; + } + + @Override + public void knockdowns$setKnockedDown(boolean knockedDown) { + this.knockedDown = knockedDown; + } + + @Override + public boolean knockdowns$isBeingRevived() { + return beingRevived; + } + + @Override + public void knockdowns$setBeingRevived(boolean beingRevived) { + this.beingRevived = beingRevived; + } + + @Override + public UUID knockdowns$getUuid() { + return ((PlayerEntity)(Object)this).getUuid(); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java new file mode 100644 index 0000000..5756cdf --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java @@ -0,0 +1,16 @@ +package ru.octol1ttle.knockdowns.common.mixin.client; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import net.minecraft.client.network.ClientPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; + +@Mixin(ClientPlayerEntity.class) +public abstract class ClientPlayerEntityMixin { + @ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN")) + private boolean shouldSlowDown(boolean original) { + IKnockableDown self = (IKnockableDown) this; + return original || self.knockdowns$isKnockedDown(); + } +} \ No newline at end of file diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java new file mode 100644 index 0000000..0267f16 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java @@ -0,0 +1,19 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.function.Supplier; +import net.minecraft.network.PacketByteBuf; + +public abstract class KnockdownsPacket { + public KnockdownsPacket(PacketByteBuf buf) { + // Decode data into a message + } + + public KnockdownsPacket(/* args here */) { + // Message creation + } + + public abstract void encode(PacketByteBuf buf); + + public abstract void apply(Supplier contextSupplier); +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java new file mode 100644 index 0000000..fa0fc82 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java @@ -0,0 +1,69 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.UUID; +import java.util.function.Supplier; +import net.minecraft.network.PacketByteBuf; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; + +public class KnockedDownStatusPacket { + public static class SendS2C extends KnockdownsPacket { + private final UUID playerUuid; + private final boolean knockedDown; + + public SendS2C(PacketByteBuf buf) { + this(buf.readUuid(), buf.readBoolean()); + } + + public SendS2C(UUID playerUuid, boolean knockedDown) { + this.playerUuid = playerUuid; + this.knockedDown = knockedDown; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + buf.writeBoolean(this.knockedDown); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + knockableDown.knockdowns$setKnockedDown(this.knockedDown); + } + }); + } + } + + public static class RequestC2S extends KnockdownsPacket { + private final UUID playerUuid; + + public RequestC2S(PacketByteBuf buf) { + this(buf.readUuid()); + } + + public RequestC2S(UUID playerUuid) { + this.playerUuid = playerUuid; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + KnockdownsNetwork.sendToPlayer(context.getPlayer(), new SendS2C(this.playerUuid, knockableDown.knockdowns$isKnockedDown())); + } + }); + } + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java new file mode 100644 index 0000000..041d112 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java @@ -0,0 +1,40 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.function.Supplier; +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.math.Vec3d; +import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; +import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance; + +public class PlayKnockedDownSoundS2CPacket extends KnockdownsPacket { + private final double x; + private final double y; + private final double z; + + public PlayKnockedDownSoundS2CPacket(PacketByteBuf buf) { + this(buf.readDouble(), buf.readDouble(), buf.readDouble()); + } + + public PlayKnockedDownSoundS2CPacket(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeDouble(this.x); + buf.writeDouble(this.y); + buf.writeDouble(this.z); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> MinecraftClient.getInstance().getSoundManager().play( + new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), new Vec3d(this.x, this.y, this.z)) + )); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java new file mode 100644 index 0000000..e84b7da --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java @@ -0,0 +1,138 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.UUID; +import java.util.function.Supplier; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.PacketByteBuf; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; + +public class ReviveStatusPacket { + public static class SendS2C extends KnockdownsPacket { + private final UUID playerUuid; + private final boolean beingRevived; + + public SendS2C(PacketByteBuf buf) { + this(buf.readUuid(), buf.readBoolean()); + } + + public SendS2C(UUID playerUuid, boolean beingRevived) { + this.playerUuid = playerUuid; + this.beingRevived = beingRevived; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + buf.writeBoolean(this.beingRevived); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + knockableDown.knockdowns$setBeingRevived(this.beingRevived); + } + }); + } + } + + public static class SendC2S extends KnockdownsPacket { + private final UUID playerUuid; + private final boolean beingRevived; + + public SendC2S(PacketByteBuf buf) { + this(buf.readUuid(), buf.readBoolean()); + } + + public SendC2S(UUID playerUuid, boolean beingRevived) { + this.playerUuid = playerUuid; + this.beingRevived = beingRevived; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + buf.writeBoolean(this.beingRevived); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + knockableDown.knockdowns$setBeingRevived(this.beingRevived); + KnockdownsNetwork.sendToListenersAndSelf(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, this.beingRevived)); + } + }); + } + } + + public static class RequestC2S extends KnockdownsPacket { + private final UUID playerUuid; + + public RequestC2S(PacketByteBuf buf) { + this(buf.readUuid()); + } + + public RequestC2S(UUID playerUuid) { + this.playerUuid = playerUuid; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + KnockdownsNetwork.sendToPlayer(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, knockableDown.knockdowns$isBeingRevived())); + } + }); + } + } + + public static class RevivedC2S extends KnockdownsPacket { + private final UUID playerUuid; + + public RevivedC2S(PacketByteBuf buf) { + this(buf.readUuid()); + } + + public RevivedC2S(UUID playerUuid) { + this.playerUuid = playerUuid; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + PlayerEntity reviving = context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + IKnockableDown knockableDown = (IKnockableDown) reviving; + if (knockableDown == null || !knockableDown.knockdowns$isKnockedDown()) { + return; + } + + reviving.setInvulnerable(false); + reviving.setGlowing(false); + reviving.setHealth(6.0f); + + knockableDown.knockdowns$setKnockedDown(false); + KnockdownsNetwork.sendToListenersAndSelf(reviving, new KnockedDownStatusPacket.SendS2C(reviving.getUuid(), false)); + }); + } + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java new file mode 100644 index 0000000..a207925 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java @@ -0,0 +1,18 @@ +package ru.octol1ttle.knockdowns.common.registries; + +import dev.architectury.registry.registries.DeferredRegister; +import dev.architectury.registry.registries.RegistrySupplier; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; +import ru.octol1ttle.knockdowns.common.KnockdownsCommon; + +public class KnockdownsSoundEvents { + private static final DeferredRegister SOUND_EVENTS = DeferredRegister.create(KnockdownsCommon.MOD_ID, RegistryKeys.SOUND_EVENT); + public static final RegistrySupplier KNOCKED_DOWN = SOUND_EVENTS.register(KnockdownsCommon.MOD_ID, + () -> SoundEvent.of(new Identifier(KnockdownsCommon.MOD_ID, "knocked_down"))); + + public static void register() { + SOUND_EVENTS.register(); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java new file mode 100644 index 0000000..7781165 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java @@ -0,0 +1,31 @@ +package ru.octol1ttle.knockdowns.common.registries; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.sound.MovingSoundInstance; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.random.Random; + +public class KnockedDownSoundInstance extends MovingSoundInstance { + private final Vec3d pos; + + public KnockedDownSoundInstance(SoundEvent sound, Vec3d pos) { + super(sound, SoundCategory.MASTER, Random.create(0L)); + this.pos = pos; + this.relative = true; + } + + @Override + public void tick() { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player == null) { + throw new IllegalStateException(); + } + Vec3d vec = pos.subtract(player.getPos()).normalize(); + this.x = vec.x; + this.y = vec.y; + this.z = vec.z; + } +} diff --git a/common/src/main/resources/assets/knockdowns/lang/en_us.json b/common/src/main/resources/assets/knockdowns/lang/en_us.json index 2805821..256bd9b 100644 --- a/common/src/main/resources/assets/knockdowns/lang/en_us.json +++ b/common/src/main/resources/assets/knockdowns/lang/en_us.json @@ -1,3 +1,102 @@ { - "item.knockdowns.example_item": "Example Item" + "knockdown.attack.anvil": "%1$s was knocked down by a falling anvil", + "knockdown.attack.anvil.player": "%1$s was knocked down by a falling anvil while fighting %2$s", + "knockdown.attack.arrow": "%1$s was knocked down due to an arrow fired by %2$s", + "knockdown.attack.arrow.item": "%1$s was knocked down due to an arrow fired by %2$s using %3$s", + "knockdown.attack.badRespawnPoint.link": "Intentional Game Design", + "knockdown.attack.badRespawnPoint.message": "%1$s was knocked down by %2$s", + "knockdown.attack.cactus": "%1$s was knocked down by a cactus", + "knockdown.attack.cactus.player": "%1$s was knocked down into a cactus while trying to escape %2$s", + "knockdown.attack.cramming": "%1$s was knocked down by squishing", + "knockdown.attack.cramming.player": "%1$s was knocked down due to being squashed by %2$s", + "knockdown.attack.dragonBreath": "%1$s was knocked down by dragon's breath", + "knockdown.attack.dragonBreath.player": "%1$s was knocked down by dragon's breath by %2$s", + "knockdown.attack.drown": "%1$s was knocked down by drowning", + "knockdown.attack.drown.player": "%1$s was knocked down by drowning while trying to escape %2$s", + "knockdown.attack.dryout": "%1$s was knocked down by dehydration", + "knockdown.attack.dryout.player": "%1$s was knocked down by dehydration while trying to escape %2$s", + "knockdown.attack.even_more_magic": "%1$s was knocked down by even more magic", + "knockdown.attack.explosion": "%1$s was knocked down due to an explosion", + "knockdown.attack.explosion.player": "%1$s was knocked down due to an explosion caused by %2$s", + "knockdown.attack.explosion.player.item": "%1$s was knocked down due to an explosion caused by %2$s using %3$s", + "knockdown.attack.fall": "%1$s was knocked down by a fall", + "knockdown.attack.fall.player": "%1$s was knocked down by a fall while trying to escape %2$s", + "knockdown.attack.fallingBlock": "%1$s was knocked down by a falling block", + "knockdown.attack.fallingBlock.player": "%1$s was knocked down by a falling block while fighting %2$s", + "knockdown.attack.fallingStalactite": "%1$s was knocked down by a falling stalactite", + "knockdown.attack.fallingStalactite.player": "%1$s was knocked down by a falling stalactite while fighting %2$s", + "knockdown.attack.fireball": "%1$s was knocked down due to a fireball fired by %2$s", + "knockdown.attack.fireball.item": "%1$s was knocked down due to a fireball fired by %2$s using %3$s", + "knockdown.attack.fireworks": "%1$s was knocked down with a bang", + "knockdown.attack.fireworks.item": "%1$s was knocked down with a bang due to a firework fired from %3$s by %2$s", + "knockdown.attack.fireworks.player": "%1$s was knocked down with a bang while fighting %2$s", + "knockdown.attack.flyIntoWall": "%1$s was knocked down by kinetic energy", + "knockdown.attack.flyIntoWall.player": "%1$s was knocked down by kinetic energy while trying to escape %2$s", + "knockdown.attack.freeze": "%1$s was knocked down by freezing", + "knockdown.attack.freeze.player": "%1$s was knocked down by freezing by %2$s", + "knockdown.attack.generic": "%1$s was knocked down", + "knockdown.attack.generic.player": "%1$s was knocked down because of %2$s", + "knockdown.attack.genericKill": "%1$s was knocked down", + "knockdown.attack.genericKill.player": "%1$s was knocked down while fighting %2$s", + "knockdown.attack.hotFloor": "%1$s was knocked down by the lava floor", + "knockdown.attack.hotFloor.player": "%1$s was knocked down by the lava floor due to %2$s", + "knockdown.attack.indirectMagic": "%1$s was knocked down by %2$s using magic", + "knockdown.attack.indirectMagic.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.inFire": "%1$s was knocked down by fire", + "knockdown.attack.inFire.player": "%1$s was knocked down by fire while fighting %2$s", + "knockdown.attack.inWall": "%1$s was knocked down by suffocation", + "knockdown.attack.inWall.player": "%1$s was knocked down by suffocation while fighting %2$s", + "knockdown.attack.lava": "%1$s was knocked down by lava", + "knockdown.attack.lava.player": "%1$s was knocked down by lava while trying to escape %2$s", + "knockdown.attack.lightningBolt": "%1$s was knocked down by lightning", + "knockdown.attack.lightningBolt.player": "%1$s was knocked down by lightning while fighting %2$s", + "knockdown.attack.magic": "%1$s was knocked down by magic", + "knockdown.attack.magic.player": "%1$s was knocked down by magic while trying to escape %2$s", + "knockdown.attack.message_too_long": "Actually, the message was too long to deliver fully. Sorry! Here's a stripped version: %s", + "knockdown.attack.mob": "%1$s was knocked down by %2$s", + "knockdown.attack.mob.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.onFire": "%1$s was knocked down by fire", + "knockdown.attack.onFire.item": "%1$s was knocked down by fire while fighting %2$s wielding %3$s", + "knockdown.attack.onFire.player": "%1$s was knocked down by fire while fighting %2$s", + "knockdown.attack.outOfWorld": "%1$s was knocked down by the void", + "knockdown.attack.outOfWorld.player": "%1$s was knocked down by the void while fighting %2$s", + "knockdown.attack.outsideBorder": "%1$s was knocked down by the world border", + "knockdown.attack.outsideBorder.player": "%1$s was knocked down by the world border while fighting %2$s", + "knockdown.attack.player": "%1$s was knocked down by %2$s", + "knockdown.attack.player.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.sonic_boom": "%1$s was knocked down by a sonically-charged shriek", + "knockdown.attack.sonic_boom.item": "%1$s was knocked down by a sonically-charged shriek while trying to escape %2$s wielding %3$s", + "knockdown.attack.sonic_boom.player": "%1$s was knocked down by a sonically-charged shriek while trying to escape %2$s", + "knockdown.attack.stalagmite": "%1$s was knocked down by a stalagmite", + "knockdown.attack.stalagmite.player": "%1$s was knocked down by a stalagmite while fighting %2$s", + "knockdown.attack.starve": "%1$s was knocked down by starving", + "knockdown.attack.starve.player": "%1$s was knocked down by starving while fighting %2$s", + "knockdown.attack.sting": "%1$s was knocked down by stings", + "knockdown.attack.sting.item": "%1$s was knocked down by stings by %2$s using %3$s", + "knockdown.attack.sting.player": "%1$s was knocked down by stings by %2$s", + "knockdown.attack.sweetBerryBush": "%1$s was knocked down by a sweet berry bush", + "knockdown.attack.sweetBerryBush.player": "%1$s was knocked down by a sweet berry bush while trying to escape %2$s", + "knockdown.attack.thorns": "%1$s was knocked down while trying to hurt %2$s", + "knockdown.attack.thorns.item": "%1$s was knocked down by %3$s while trying to hurt %2$s", + "knockdown.attack.thrown": "%1$s was knocked down due to being thrown by %2$s", + "knockdown.attack.thrown.item": "%1$s was knocked down due to being thrown by %2$s using %3$s", + "knockdown.attack.trident": "%1$s was knocked down due to being impaled by %2$s", + "knockdown.attack.trident.item": "%1$s was knocked down due to being impaled by %2$s with %3$s", + "knockdown.attack.wither": "%1$s was knocked down by withering", + "knockdown.attack.wither.player": "%1$s was knocked down by withering while fighting %2$s", + "knockdown.attack.witherSkull": "%1$s was knocked down by a skull from %2$s", + "knockdown.attack.witherSkull.item": "%1$s was knocked down by a skull from %2$s using %3$s", + "knockdown.fell.accident.generic": "%1$s was knocked down by a fall", + "knockdown.fell.accident.ladder": "%1$s was knocked down by a fall off a ladder", + "knockdown.fell.accident.other_climbable": "%1$s was knocked down by a fall while climbing", + "knockdown.fell.accident.scaffolding": "%1$s was knocked down by a fall off scaffolding", + "knockdown.fell.accident.twisting_vines": "%1$s was knocked down by a fall off some twisting vines", + "knockdown.fell.accident.vines": "%1$s was knocked down by a fall off some vines", + "knockdown.fell.accident.weeping_vines": "%1$s was knocked down by a fall off some weeping vines", + "knockdown.fell.assist": "%1$s was doomed to get knocked down by %2$s", + "knockdown.fell.assist.item": "%1$s was doomed to get knocked down by %2$s using %3$s", + "knockdown.fell.finish": "%1$s fell too far and was knocked down by %2$s", + "knockdown.fell.finish.item": "%1$s fell too far and was knocked down by %2$s using %3$s", + "knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall", + "subtitles.knockdowns.knocked_down": "Player knocked down" } \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/lang/ru_ru.json b/common/src/main/resources/assets/knockdowns/lang/ru_ru.json new file mode 100644 index 0000000..5cdd965 --- /dev/null +++ b/common/src/main/resources/assets/knockdowns/lang/ru_ru.json @@ -0,0 +1,102 @@ +{ + "knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней", + "knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s", + "knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s", + "knockdown.attack.arrow.item": "%1$s тяжело ранен стрелой %2$s из %3$s", + "knockdown.attack.badRespawnPoint.link": "жестокими правилами игры", + "knockdown.attack.badRespawnPoint.message": "%1$s был тяжело ранен %2$s", + "knockdown.attack.cactus": "%1$s был тяжело ранен кактусом", + "knockdown.attack.cactus.player": "%1$s был тяжело ранен кактусом, спасаясь от %2$s", + "knockdown.attack.cramming": "%1$s был тяжело ранен расплющиванием", + "knockdown.attack.cramming.player": "%1$s был тяжело ранен расплющиванием %2$s", + "knockdown.attack.dragonBreath": "%1$s был тяжело ранен в драконьем дыхании", + "knockdown.attack.dragonBreath.player": "%1$s был тяжело ранен в драконьем дыхании из-за %2$s", + "knockdown.attack.drown": "%1$s был тяжело ранен утоплением", + "knockdown.attack.drown.player": "%1$s был тяжело ранен утоплением, спасаясь от %2$s", + "knockdown.attack.dryout": "%1$s был тяжело ранен обезвоживанием", + "knockdown.attack.dryout.player": "%1$s был тяжело ранен обезвоживанием, спасаясь от %2$s", + "knockdown.attack.even_more_magic": "%1$s был тяжело ранен неизведанной магией", + "knockdown.attack.explosion": "%1$s был тяжело ранен взрывом", + "knockdown.attack.explosion.player": "%1$s был тяжело ранен взрывом %2$s", + "knockdown.attack.explosion.player.item": "%1$s был тяжело ранен взрывом %2$s с помощью %3$s", + "knockdown.attack.fall": "%1$s был тяжело ранен падением", + "knockdown.attack.fall.player": "%1$s был тяжело ранен падением, спасаясь от %2$s", + "knockdown.attack.fallingBlock": "%1$s был тяжело ранен упавшим блоком", + "knockdown.attack.fallingBlock.player": "%1$s был тяжело ранен упавшим блоком, пока боролся с %2$s", + "knockdown.attack.fallingStalactite": "%1$s был тяжело ранен обрушившимся сталактитом", + "knockdown.attack.fallingStalactite.player": "%1$s был тяжело ранен обрушившимся сталактитом, пока боролся с %2$s", + "knockdown.attack.fireball": "%1$s был тяжело ранен файерболом %2$s", + "knockdown.attack.fireball.item": "%1$s был тяжело ранен файерболом %2$s с помощью %3$s", + "knockdown.attack.fireworks": "%1$s был тяжело ранен взрывом фейерверка", + "knockdown.attack.fireworks.item": "%1$s был тяжело ранен взрывом фейерверка %2$s, выпущенного из %3$s", + "knockdown.attack.fireworks.player": "%1$s был тяжело ранен взрывом фейерверка, пока боролся с %2$s", + "knockdown.attack.flyIntoWall": "%1$s был тяжело ранен кинетической энергией", + "knockdown.attack.flyIntoWall.player": "%1$s был тяжело ранен кинетической энергией, спасаясь от %2$s", + "knockdown.attack.freeze": "%1$s был тяжело ранен замерзанием", + "knockdown.attack.freeze.player": "%1$s был тяжело ранен замерзанием благодаря %2$s", + "knockdown.attack.generic": "%1$s тяжело ранен", + "knockdown.attack.generic.player": "%1$s тяжело ранен из-за %2$s", + "knockdown.attack.genericKill": "%1$s тяжело ранен", + "knockdown.attack.genericKill.player": "%1$s был тяжело ранен, пока боролся с %2$s", + "knockdown.attack.hotFloor": "%1$s был тяжело ранен, обнаружив, что пол — это лава", + "knockdown.attack.hotFloor.player": "%1$s был тяжело ранен, так как зашёл в опасную зону из-за %2$s", + "knockdown.attack.inFire": "%1$s был тяжело ранен в огне", + "knockdown.attack.inFire.player": "%1$s был тяжело ранен в огне, пока боролся с %2$s", + "knockdown.attack.inWall": "%1$s был тяжело ранен погребением заживо", + "knockdown.attack.inWall.player": "%1$s был тяжело ранен погребением заживо, пока боролся с %2$s", + "knockdown.attack.indirectMagic": "%1$s был тяжело ранен %2$s с помощью магии", + "knockdown.attack.indirectMagic.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.lava": "%1$s был тяжело ранен лавой", + "knockdown.attack.lava.player": "%1$s был тяжело ранен лавой, убегая от %2$s", + "knockdown.attack.lightningBolt": "%1$s был тяжело ранен молнией", + "knockdown.attack.lightningBolt.player": "%1$s был тяжело ранен молнией, пока сражался с %2$s", + "knockdown.attack.magic": "%1$s был тяжело ранен магией", + "knockdown.attack.magic.player": "%1$s был тяжело ранен магией, убегая от %2$s", + "knockdown.attack.message_too_long": "Сообщение слишком длинное для доставки. Извините! Вот сокращённая версия: %s", + "knockdown.attack.mob": "%1$s был тяжело ранен %2$s", + "knockdown.attack.mob.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.onFire": "%1$s был тяжело ранен огнём", + "knockdown.attack.onFire.item": "%1$s был тяжело ранен огнём, пока боролся с %2$s, с помощью %3$s", + "knockdown.attack.onFire.player": "%1$s был тяжело ранен огнём, пока боролся с %2$s", + "knockdown.attack.outOfWorld": "%1$s был тяжело ранен пустотой", + "knockdown.attack.outOfWorld.player": "%1$s был тяжело ранен пустотой, благодаря %2$s", + "knockdown.attack.outsideBorder": "%1$s был тяжело ранен пределами этого мира", + "knockdown.attack.outsideBorder.player": "%1$s был тяжело ранен пределами этого мира, пока боролся с %2$s", + "knockdown.attack.player": "%1$s был тяжело ранен %2$s", + "knockdown.attack.player.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.sonic_boom": "%1$s был тяжело ранен звуковым зарядом", + "knockdown.attack.sonic_boom.item": "%1$s был тяжело ранен звуковым ударом, спасаясь от %2$s c %3$s", + "knockdown.attack.sonic_boom.player": "%1$s был тяжело ранен звуковым ударом, спасаясь от %2$s", + "knockdown.attack.stalagmite": "%1$s был тяжело ранен сталагмитом", + "knockdown.attack.stalagmite.player": "%1$s был тяжело ранен сталагмитом, пока боролся с %2$s", + "knockdown.attack.starve": "%1$s был тяжело ранен голодом", + "knockdown.attack.starve.player": "%1$s был тяжело ранен голодом, пока боролся с %2$s", + "knockdown.attack.sting": "%1$s был тяжело ранен изжалением", + "knockdown.attack.sting.item": "%1$s был тяжело ранен изжалением %2$s с помощью %3$s", + "knockdown.attack.sting.player": "%1$s был тяжело ранен изжалением %2$s", + "knockdown.attack.sweetBerryBush": "%1$s был тяжело ранен исколением в кустах сладких ягод", + "knockdown.attack.sweetBerryBush.player": "%1$s был тяжело ранен исколением в кустах сладких ягод, спасаясь от %2$s", + "knockdown.attack.thorns": "%1$s был тяжело ранен, пытаясь навредить %2$s", + "knockdown.attack.thorns.item": "%1$s был тяжело ранен %3$s, пытаясь навредить %2$s", + "knockdown.attack.thrown": "%1$s был тяжело ранен %2$s", + "knockdown.attack.thrown.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.trident": "%1$s был тяжело ранен пронзанием %2$s", + "knockdown.attack.trident.item": "%1$s был тяжело ранен пронзанием %2$s с помощью %3$s", + "knockdown.attack.wither": "%1$s был тяжело ранен иссушением", + "knockdown.attack.wither.player": "%1$s был тяжело ранен иссушением, пока боролся с %2$s", + "knockdown.attack.witherSkull": "%1$s был тяжело ранен поражением черепом из %2$s", + "knockdown.attack.witherSkull.item": "%1$s был тяжело ранен поражением черепом из %2$s с помощью %3$s", + "knockdown.fell.accident.generic": "%1$s был тяжело ранен падением", + "knockdown.fell.accident.ladder": "%1$s был тяжело ранен падением с лестницы", + "knockdown.fell.accident.other_climbable": "%1$s был тяжело ранен падением", + "knockdown.fell.accident.scaffolding": "%1$s был тяжело ранен падением с подмосток", + "knockdown.fell.accident.twisting_vines": "%1$s был тяжело ранен падением с вьющейся лозы", + "knockdown.fell.accident.vines": "%1$s был тяжело ранен падением с лианы", + "knockdown.fell.accident.weeping_vines": "%1$s был тяжело ранен падением с плакучей лозы", + "knockdown.fell.assist": "%1$s был тяжело ранен падением благодаря %2$s", + "knockdown.fell.assist.item": "%1$s был тяжело ранен падением благодаря %2$s с помощью %3$s", + "knockdown.fell.finish": "%1$s упал с высоты и был тяжело ранен %2$s", + "knockdown.fell.finish.item": "%1$s упал с высоты и был тяжело ранен %2$s с помощью %3$s", + "knockdown.fell.killer": "%1$s был тяжело ранен падением", + "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/sounds.json b/common/src/main/resources/assets/knockdowns/sounds.json new file mode 100644 index 0000000..52d77b8 --- /dev/null +++ b/common/src/main/resources/assets/knockdowns/sounds.json @@ -0,0 +1,10 @@ +{ + "knocked_down": { + "subtitle": "subtitles.knockdowns.knocked_down", + "sounds": [ + { + "name": "knockdowns:knocked_down" + } + ] + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg b/common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ccb17cc9f69e393851008a80c58f865057b229c6 GIT binary patch literal 26287 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/knockdowns-common.mixins.json b/common/src/main/resources/knockdowns-common.mixins.json index fd7619d..8eef057 100644 --- a/common/src/main/resources/knockdowns-common.mixins.json +++ b/common/src/main/resources/knockdowns-common.mixins.json @@ -1,12 +1,13 @@ { "required": true, - "package": "net.knockdowns.mixin", + "package": "ru.octol1ttle.knockdowns.common.mixin", "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "client": [ - "ru.octol1ttle.knockdowns.mixin.MixinTitleScreen" + "client.ClientPlayerEntityMixin" ], "mixins": [ + "PlayerEntityMixin" ], "injectors": { "defaultRequire": 1 diff --git a/common/src/main/resources/knockdowns.accesswidener b/common/src/main/resources/knockdowns.accesswidener index 13268c3..4dddd12 100644 --- a/common/src/main/resources/knockdowns.accesswidener +++ b/common/src/main/resources/knockdowns.accesswidener @@ -1 +1,4 @@ -accessWidener v2 named \ No newline at end of file +accessWidener v2 named + +accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage entityTrackers Lit/unimi/dsi/fastutil/ints/Int2ObjectMap; +accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage$EntityTracker listeners Ljava/util/Set; \ No newline at end of file diff --git a/fabric/src/main/resources/knockdowns.mixins.json b/fabric/src/main/resources/knockdowns.mixins.json index c33ed01..76a92ed 100644 --- a/fabric/src/main/resources/knockdowns.mixins.json +++ b/fabric/src/main/resources/knockdowns.mixins.json @@ -1,6 +1,6 @@ { "required": true, - "package": "net.knockdowns.mixin.fabric", + "package": "ru.octol1ttle.knockdowns.fabric.mixin", "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "client": [ diff --git a/forge/build.gradle b/forge/build.gradle index 8f7a732..490b2f1 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -34,6 +34,8 @@ dependencies { common(project(path: ":common", configuration: "namedElements")) { transitive false } shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false } + + implementation(include("io.github.llamalad7:mixinextras-forge:0.3.3")) } processResources { diff --git a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java index a8e8643..e71aa25 100644 --- a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java +++ b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java @@ -1,9 +1,9 @@ package ru.octol1ttle.knockdowns.forge; import dev.architectury.platform.forge.EventBuses; -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import ru.octol1ttle.knockdowns.common.KnockdownsCommon; @Mod(KnockdownsCommon.MOD_ID) public class KnockdownsForge { diff --git a/forge/src/main/resources/knockdowns.mixins.json b/forge/src/main/resources/knockdowns.mixins.json index 66779c8..bc08964 100644 --- a/forge/src/main/resources/knockdowns.mixins.json +++ b/forge/src/main/resources/knockdowns.mixins.json @@ -1,6 +1,6 @@ { "required": true, - "package": "net.knockdowns.mixin.forge", + "package": "ru.octol1ttle.knockdowns.forge.mixin", "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "client": [ diff --git a/gradle.properties b/gradle.properties index a7ea28d..c3bc8bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ maven_group=ru.octol1ttle.knockdowns architectury_version=9.1.12 fabric_api_version=0.90.4+1.20.1 -fabric_loader_version=0.15.4 +fabric_loader_version=0.15.5 forge_version=1.20.1-47.2.0