diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/ServerConfig.java b/crafting-dead-core/src/main/java/com/craftingdead/core/ServerConfig.java index c038ab301..a554b20ae 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/ServerConfig.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/ServerConfig.java @@ -37,6 +37,16 @@ public class ServerConfig { public final ForgeConfigSpec.EnumValue hitMarkerMode; public final ForgeConfigSpec.BooleanValue killSoundEnabled; + // ================================================================================ + // Game-Settings Values + // ================================================================================ + + public final ForgeConfigSpec.BooleanValue overrideMinecraftInventory; + public final ForgeConfigSpec.BooleanValue overrideMinecraftHotbar; + public final ForgeConfigSpec.BooleanValue allowOffhandSwap; + public final ForgeConfigSpec.BooleanValue allowTogglePerspective; + public final ForgeConfigSpec.DoubleValue handcuffDamageChance; + // ================================================================================ // Burst-fire Values // ================================================================================ @@ -157,6 +167,34 @@ private ServerConfig(ForgeConfigSpec.Builder builder) { .translation("options.craftingdead.server.kill_sound_enabled") .define("killSoundEnabled", true); + // Game-Settings configuration + builder + .comment("General Game-Settings") + .push("game-settings"); + { + this.overrideMinecraftInventory = builder + .translation("options.craftingdead.server.override_minecraft_inventory") + .comment("If true overrides the default Minecraft inventory with the Crafting Dead inventory (Opens with \"E\")") + .define("overrideWithCraftingDeadInventory", false); + this.overrideMinecraftHotbar = builder + .translation("options.craftingdead.server.override_minecraft_hotbar") + .comment("If true overrides the default Minecraft hotbar") + .define("overrideMinecraftHotbar", false); + this.allowOffhandSwap = builder + .translation("options.craftingdead.server.allow_offhand_swap") + .comment("If true players are allowed to swap items to the offhand") + .define("allowOffhandSwap", true); + this.allowTogglePerspective = builder + .translation("options.craftingdead.server.allow_toggle_perspective") + .comment("If true players are allowed to toggle their perspective") + .define("allowTogglePerspective", true); + this.handcuffDamageChance = builder + .translation("options.craftingdead.server.handcuff_damage_chance") + .comment("The Chance that the player damages the handcuff") + .defineInRange("handcuffDamageChance", 0.4F, 0.1F, 1.0F); + } + builder.pop(); + // Burst-fire configuration builder .comment("Some guns allow 'burstfire', where it can fire multiple shots at the same time", diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/client/ClientDist.java b/crafting-dead-core/src/main/java/com/craftingdead/core/client/ClientDist.java index 2da2b37ba..32cf162e8 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/client/ClientDist.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/client/ClientDist.java @@ -18,8 +18,16 @@ package com.craftingdead.core.client; +import com.craftingdead.core.network.message.play.DamageHandcuffsMessage; +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.systems.RenderSystem; import java.util.Optional; import java.util.Set; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import com.craftingdead.core.CraftingDead; @@ -251,7 +259,7 @@ public ItemRenderDispatcher getItemRendererManager() { * (contains both client and server code) don't access fields directly from {@link Minecraft} as * it will cause class loading problems. To safely access {@link ClientPlayerEntity} in a * multi-sided environment, use {@link #getPlayerExtension()}. - * + * * @return {@link Minecraft} */ public Minecraft getMinecraft() { @@ -500,9 +508,11 @@ public void handleClientTick(TickEvent.ClientTickEvent event) { // Update tutorial while (OPEN_EQUIPMENT_MENU.consumeClick()) { - NetworkChannel.PLAY.getSimpleChannel().sendToServer(new OpenEquipmentMenuMessage()); - if (this.minecraft.getTutorial().instance instanceof ModTutorialStepInstance) { - ((ModTutorialStepInstance) this.minecraft.getTutorial().instance).openEquipmentMenu(); + if (!player.isHandcuffed()) { + NetworkChannel.PLAY.getSimpleChannel().sendToServer(new OpenEquipmentMenuMessage()); + if (this.minecraft.getTutorial().instance instanceof ModTutorialStepInstance) { + ((ModTutorialStepInstance) this.minecraft.getTutorial().instance).openEquipmentMenu(); + } } } TutorialSteps currentTutorialStep = this.minecraft.options.tutorialStep; @@ -597,6 +607,14 @@ public void handleRenderGameOverlayPreLayer(RenderGameOverlayEvent.PreLayer even || overlay == ForgeIngameGui.AIR_LEVEL_ELEMENT || overlay == ForgeIngameGui.ARMOR_LEVEL_ELEMENT) { event.setCanceled(player.isCombatModeEnabled()); + + if (overlay == ForgeIngameGui.HOTBAR_ELEMENT + && ServerConfig.instance.overrideMinecraftHotbar.get() + && !player.isCombatModeEnabled()) { + event.setCanceled(true); + this.renderHotbar(event.getMatrixStack(), event.getWindow()); + } + } else if (overlay == ForgeIngameGui.CROSSHAIR_ELEMENT) { var aiming = player.mainHandItem().getCapability(Scope.CAPABILITY) .map(scope -> scope.isScoping(player)) @@ -635,7 +653,8 @@ public void handleRenderGameOverlayPre(RenderGameOverlayEvent.Pre event) { event.getWindow().getGuiScaledWidth(), event.getWindow().getGuiScaledHeight(), event.getPartialTicks()); } - default -> {} + default -> { + } } } @@ -731,19 +750,47 @@ private void updateAdrenalineShader(float partialTicks) { @SubscribeEvent public void handleScreenOpen(ScreenOpenEvent event) { - // Prevents current screen being closed before new one opens. + if (this.minecraft == null || this.minecraft.player == null) { + return; + } + + final var player = this.minecraft.player; + var playerExtension = PlayerExtension.get(player); + + // Prevents current screen from being closed before new one opens. if (this.minecraft.screen instanceof EquipmentScreen screen && event.getScreen() == null && screen.isTransitioning()) { event.setCanceled(true); } + + // Prevents the player from opening the inventory if handcuffed + if (playerExtension != null && playerExtension.isHandcuffed()) { + if (event.getScreen() instanceof InventoryScreen && isSurvivalMode()) { + event.setCanceled(true); + return; + } + } + + // Allows overriding the default inventory with the crafting dead inventory + if (event.getScreen() instanceof InventoryScreen && isSurvivalMode() + && ServerConfig.instance.overrideMinecraftInventory.get()) { + event.setCanceled(true); + NetworkChannel.PLAY.getSimpleChannel().sendToServer(new OpenEquipmentMenuMessage()); + } } @SubscribeEvent public void handleRenderHand(RenderHandEvent event) { - final var player = this.getCameraPlayer(); - if (player != null) { - event.setCanceled(player.isHandcuffed()); + final var player = this.minecraft.player; + final var cameraPlayer = this.getCameraPlayer(); + + if (cameraPlayer != null) { + event.setCanceled(cameraPlayer.isHandcuffed()); + } + + if (player != null && player.hasEffect(ModMobEffects.PARACHUTE.get())) { + renderParachute(event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight()); } } @@ -755,6 +802,26 @@ public void handleClickInput(InputEvent.ClickInputEvent event) { } } + @SubscribeEvent + public void onMouseInput(InputEvent.MouseInputEvent event) { + if (this.minecraft.options.keyUse.matchesMouse(event.getButton()) + && event.getAction() == GLFW.GLFW_PRESS) { + if (this.minecraft.player != null && this.minecraft.screen == null) { + NetworkChannel.PLAY.getSimpleChannel().sendToServer(new DamageHandcuffsMessage()); + } + } + } + + @SubscribeEvent + public void onKeyInput(InputEvent.KeyInputEvent event) { + if (this.minecraft.options.keyUse.matches(event.getKey(), event.getScanCode()) + && event.getAction() == GLFW.GLFW_PRESS) { + if (this.minecraft.player != null && this.minecraft.screen == null) { + NetworkChannel.PLAY.getSimpleChannel().sendToServer(new DamageHandcuffsMessage()); + } + } + } + // ================================================================================ // Client-only helper methods // ================================================================================ @@ -811,4 +878,73 @@ public static void renderArmWithClothing(PlayerRenderer renderer, PoseStack pose OverlayTexture.NO_OVERLAY); } } + + private void renderParachute(PoseStack poseStack, MultiBufferSource bufferSource, + int packedLight) { + poseStack.pushPose(); + poseStack.translate(0.0D, -1.08D, -1.0D); + poseStack.mulPose(Vector3f.XP.rotationDegrees(180.0F)); + + var vertexConsumer = ItemRenderer.getArmorFoilBuffer( + bufferSource, + RenderType.armorCutoutNoCull( + new ResourceLocation(CraftingDead.ID, "textures/entity/parachute.png") + ), + false, + false + ); + + EntityModelSet entityModelSet = Minecraft.getInstance().getEntityModels(); + ModelPart parachuteModel = entityModelSet.bakeLayer(ModModelLayers.PARACHUTE); + parachuteModel.render(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY); + poseStack.popPose(); + } + + private void renderHotbar(PoseStack poseStack, Window window) { + var player = this.getPlayerExtension().orElse(null); + + assert player != null; + if (player.isCombatModeEnabled()) { + return; + } + + int screenWidth = window.getGuiScaledWidth(); + int screenHeight = window.getGuiScaledHeight(); + + int xPos = (screenWidth / 2) - 91; + int yPos = screenHeight - 22; + + this.minecraft.getProfiler().push("hotbar"); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, + new ResourceLocation(CraftingDead.ID, "textures/gui/container/widgets.png")); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + this.minecraft.gui.blit(poseStack, xPos, yPos, 0, 0, 182, 22); + + int selectedSlot = player.entity().getInventory().selected; + int selectedXPos = xPos + selectedSlot * 20 - 1; + + this.minecraft.gui.blit(poseStack, selectedXPos, yPos - 1, 0, 22, 24, 24); + + var itemRenderer = this.minecraft.getItemRenderer(); + + for (int i = 0; i < 9; ++i) { + int slotXPos = xPos + i * 20 + 3; + int slotYPos = yPos + 3; + ItemStack itemStack = player.entity().getInventory().getItem(i); + + itemRenderer.renderAndDecorateItem(itemStack, slotXPos, slotYPos); + itemRenderer.renderGuiItemDecorations(this.minecraft.font, itemStack, slotXPos, slotYPos); + } + + RenderSystem.disableBlend(); + this.minecraft.getProfiler().pop(); + } + + private boolean isSurvivalMode() { + return this.minecraft.player != null && !this.minecraft.player.getAbilities().instabuild + && !this.minecraft.player.isSpectator(); + } } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/client/renderer/entity/layers/ParachuteLayer.java b/crafting-dead-core/src/main/java/com/craftingdead/core/client/renderer/entity/layers/ParachuteLayer.java index dbb7bd2b2..ce53838c2 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/client/renderer/entity/layers/ParachuteLayer.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/client/renderer/entity/layers/ParachuteLayer.java @@ -37,6 +37,7 @@ import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; public class ParachuteLayer> extends RenderLayer { @@ -52,7 +53,7 @@ public ParachuteLayer(RenderLayerParent entityRenderer, EntityModelSet ent } @Override - public void render(PoseStack poseStack, MultiBufferSource renderTypeBuffer, int packedLight, + public void render(@NotNull PoseStack poseStack, @NotNull MultiBufferSource renderTypeBuffer, int packedLight, T livingEntity, float limbSwing, float limbSwingAmount, float partialTicks, float ageTicks, float headYaw, float headPitch) { if (livingEntity.hasEffect(ModMobEffects.PARACHUTE.get())) { @@ -90,40 +91,46 @@ public static LayerDefinition createParachuteBodyLayer() { PartPose.offsetAndRotation(-30.9324F, -50.5458F, 0.0F, 0.0F, 0.0F, -0.3491F)); anchor.addOrReplaceChild("cube_r3", + CubeListBuilder.create() + .texOffs(0, 0) + .addBox(-8.0F, -1.0F, -8.0F, 16.0F, 1.0F, 16.0F), + PartPose.offsetAndRotation(15.7048F, -54.626F, 0.0F, 0.0F, 0.0F, 0.1745F)); + + anchor.addOrReplaceChild("cube_r4", + CubeListBuilder.create() + .texOffs(0, 17) + .addBox(-8.0F, -1.0F, -8.0F, 16.0F, 1.0F, 16.0F), + PartPose.offsetAndRotation(30.9324F, -50.5458F, 0.0F, 0.0F, 0.0F, 0.3491F)); + + anchor.addOrReplaceChild("cube_r5", CubeListBuilder.create() .texOffs(0, 51) .addBox(-0.4226F, -0.1075F, -0.1574F, 1.0F, 43.0F, 1.0F), PartPose.offsetAndRotation(-37.0F, -48.5F, -7.0F, 0.1309F, 0.0F, -0.8727F)); - anchor.addOrReplaceChild("cube_r4", + anchor.addOrReplaceChild("cube_r6", CubeListBuilder.create() .texOffs(4, 51) .addBox(-0.4226F, -0.1075F, -0.8426F, 1.0F, 43.0F, 1.0F), PartPose.offsetAndRotation(-37.0F, -48.5F, 7.0F, -0.1309F, 0.0F, -0.8727F)); - anchor.addOrReplaceChild("cube_r5", + anchor.addOrReplaceChild("cube_r7", CubeListBuilder.create() .texOffs(8, 51) .addBox(-0.5774F, -0.1075F, -0.8426F, 1.0F, 43.0F, 1.0F), PartPose.offsetAndRotation(37.0F, -48.5F, 7.0F, -0.1309F, 0.0F, 0.8727F)); - anchor.addOrReplaceChild("cube_r6", - CubeListBuilder.create() - .texOffs(0, 34) - .addBox(-8.0F, -1.0F, -8.0F, 16.0F, 1.0F, 16.0F), - PartPose.offsetAndRotation(15.7048F, -54.626F, 0.0F, 0.0F, 0.0F, 0.1745F)); - - anchor.addOrReplaceChild("cube_r7", + anchor.addOrReplaceChild("cube_r8", CubeListBuilder.create() - .texOffs(12, 51) - .addBox(-0.5774F, -0.1075F, -0.1574F, 1.0F, 43.0F, 1.0F), + .texOffs(0, 51) + .addBox(-0.4226F, -0.1075F, -0.1574F, 1.0F, 43.0F, 1.0F), PartPose.offsetAndRotation(37.0F, -48.5F, -7.0F, 0.1309F, 0.0F, 0.8727F)); - anchor.addOrReplaceChild("cube_r8", + anchor.addOrReplaceChild("cube_r9", CubeListBuilder.create() - .texOffs(48, 48) - .addBox(-8.0F, -1.0F, -8.0F, 16.0F, 1.0F, 16.0F), - PartPose.offsetAndRotation(30.9324F, -50.5458F, 0.0F, 0.0F, 0.0F, 0.3491F)); + .texOffs(4, 51) + .addBox(-0.4226F, -0.1075F, -0.8426F, 1.0F, 43.0F, 1.0F), + PartPose.offsetAndRotation(37.0F, -48.5F, 7.0F, -0.1309F, 0.0F, 0.8727F)); return LayerDefinition.create(mesh, 128, 128); } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/mixin/ItemEntityMixin.java b/crafting-dead-core/src/main/java/com/craftingdead/core/mixin/ItemEntityMixin.java new file mode 100644 index 000000000..66e30a020 --- /dev/null +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/mixin/ItemEntityMixin.java @@ -0,0 +1,38 @@ +/* + * Crafting Dead + * Copyright (C) 2022 NexusNode LTD + * + * This Non-Commercial Software License Agreement (the "Agreement") is made between + * you (the "Licensee") and NEXUSNODE (BRAD HUNTER). (the "Licensor"). + * By installing or otherwise using Crafting Dead (the "Software"), you agree to be + * bound by the terms and conditions of this Agreement as may be revised from time + * to time at Licensor's sole discretion. + * + * If you do not agree to the terms and conditions of this Agreement do not download, + * copy, reproduce or otherwise use any of the source code available online at any time. + * + * https://github.com/nexusnode/crafting-dead/blob/1.18.x/LICENSE.txt + * + * https://craftingdead.net/terms.php + */ + +package com.craftingdead.core.mixin; + +import com.craftingdead.core.world.entity.extension.PlayerExtension; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +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(ItemEntity.class) +public class ItemEntityMixin { + + @Inject(method = "playerTouch", at = @At("HEAD"), cancellable = true) + public void onPlayerTouch(Player player, CallbackInfo ci) { + if (PlayerExtension.getOrThrow(player).isHandcuffed()) { + ci.cancel(); + } + } +} \ No newline at end of file diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/mixin/KeyboardHandlerMixin.java b/crafting-dead-core/src/main/java/com/craftingdead/core/mixin/KeyboardHandlerMixin.java new file mode 100644 index 000000000..a194a3c55 --- /dev/null +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/mixin/KeyboardHandlerMixin.java @@ -0,0 +1,71 @@ +/* + * Crafting Dead + * Copyright (C) 2022 NexusNode LTD + * + * This Non-Commercial Software License Agreement (the "Agreement") is made between + * you (the "Licensee") and NEXUSNODE (BRAD HUNTER). (the "Licensor"). + * By installing or otherwise using Crafting Dead (the "Software"), you agree to be + * bound by the terms and conditions of this Agreement as may be revised from time + * to time at Licensor's sole discretion. + * + * If you do not agree to the terms and conditions of this Agreement do not download, + * copy, reproduce or otherwise use any of the source code available online at any time. + * + * https://github.com/nexusnode/crafting-dead/blob/1.18.x/LICENSE.txt + * + * https://craftingdead.net/terms.php + */ + +package com.craftingdead.core.mixin; + +import com.craftingdead.core.ServerConfig; +import com.craftingdead.core.world.effect.ModMobEffects; +import com.craftingdead.core.world.entity.extension.PlayerExtension; +import com.craftingdead.core.world.item.GunItem; +import net.minecraft.client.KeyboardHandler; +import net.minecraft.client.Minecraft; +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(KeyboardHandler.class) +public class KeyboardHandlerMixin { + + @Inject(method = "keyPress", at = @At("HEAD"), cancellable = true) + private void onKeyPress(long windowPointer, int key, int scancode, int action, int modifiers, + CallbackInfo ci) { + + var minecraft = Minecraft.getInstance(); + var playerExtension = PlayerExtension.get(minecraft.player); + + if (playerExtension == null) { + return; + } + + int dropKey = minecraft.options.keyDrop.getKey().getValue(); + if (key == dropKey + && playerExtension.isHandcuffed()) { + ci.cancel(); + } + + int shiftKey = minecraft.options.keyShift.getKey().getValue(); + if (key == shiftKey + && playerExtension.entity().hasEffect(ModMobEffects.PARACHUTE.get())) { + ci.cancel(); + } + + int offhandKey = minecraft.options.keySwapOffhand.getKey().getValue(); + if (key == offhandKey + && (playerExtension.entity().getMainHandItem().getItem() instanceof GunItem + || !ServerConfig.instance.allowOffhandSwap.get())) { + ci.cancel(); + } + + int togglePerspectiveKey = minecraft.options.keyTogglePerspective.getKey().getValue(); + if (key == togglePerspectiveKey + && !ServerConfig.instance.allowTogglePerspective.get()) { + ci.cancel(); + } + } +} diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/network/NetworkChannel.java b/crafting-dead-core/src/main/java/com/craftingdead/core/network/NetworkChannel.java index 3ee008a84..64632d2a2 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/network/NetworkChannel.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/network/NetworkChannel.java @@ -21,10 +21,12 @@ import com.craftingdead.core.CraftingDead; import com.craftingdead.core.network.message.play.CancelActionMessage; import com.craftingdead.core.network.message.play.CrouchMessage; +import com.craftingdead.core.network.message.play.DamageHandcuffsMessage; import com.craftingdead.core.network.message.play.EnableCombatModeMessage; import com.craftingdead.core.network.message.play.HitMessage; import com.craftingdead.core.network.message.play.OpenEquipmentMenuMessage; import com.craftingdead.core.network.message.play.OpenStorageMessage; +import com.craftingdead.core.network.message.play.ParachuteSyncMessage; import com.craftingdead.core.network.message.play.PerformActionMessage; import com.craftingdead.core.network.message.play.SecondaryActionMessage; import com.craftingdead.core.network.message.play.SetFireModeMessage; @@ -140,6 +142,20 @@ public void registerMessages(SimpleChannel simpleChannel) { .decoder(EnableCombatModeMessage::decode) .consumer(EnableCombatModeMessage::handle) .add(); + + simpleChannel + .messageBuilder(DamageHandcuffsMessage.class, 0x0F, NetworkDirection.PLAY_TO_SERVER) + .encoder(DamageHandcuffsMessage::encode) + .decoder(DamageHandcuffsMessage::decode) + .consumer(DamageHandcuffsMessage::handle) + .add(); + + simpleChannel + .messageBuilder(ParachuteSyncMessage.class, 0x10, NetworkDirection.PLAY_TO_CLIENT) + .encoder(ParachuteSyncMessage::encode) + .decoder(ParachuteSyncMessage::decode) + .consumer(ParachuteSyncMessage::handle) + .add(); } }; @@ -156,7 +172,7 @@ public void registerMessages(SimpleChannel simpleChannel) { */ private final SimpleChannel simpleChannel; - private NetworkChannel(ResourceLocation channelName) { + NetworkChannel(ResourceLocation channelName) { this.simpleChannel = NetworkRegistry.ChannelBuilder .named(channelName) .clientAcceptedVersions(NETWORK_VERSION::equals) diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/network/message/play/DamageHandcuffsMessage.java b/crafting-dead-core/src/main/java/com/craftingdead/core/network/message/play/DamageHandcuffsMessage.java new file mode 100644 index 000000000..e9dc933c0 --- /dev/null +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/network/message/play/DamageHandcuffsMessage.java @@ -0,0 +1,53 @@ +/* + * Crafting Dead + * Copyright (C) 2022 NexusNode LTD + * + * This Non-Commercial Software License Agreement (the "Agreement") is made between + * you (the "Licensee") and NEXUSNODE (BRAD HUNTER). (the "Licensor"). + * By installing or otherwise using Crafting Dead (the "Software"), you agree to be + * bound by the terms and conditions of this Agreement as may be revised from time + * to time at Licensor's sole discretion. + * + * If you do not agree to the terms and conditions of this Agreement do not download, + * copy, reproduce or otherwise use any of the source code available online at any time. + * + * https://github.com/nexusnode/crafting-dead/blob/1.18.x/LICENSE.txt + * + * https://craftingdead.net/terms.php + */ + +package com.craftingdead.core.network.message.play; + +import com.craftingdead.core.world.entity.extension.PlayerExtension; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +public class DamageHandcuffsMessage { + + public DamageHandcuffsMessage() { + } + + public void encode(FriendlyByteBuf buf) { + } + + public static DamageHandcuffsMessage decode(FriendlyByteBuf buf) { + return new DamageHandcuffsMessage(); + } + + public void handle(Supplier contextSupplier) { + NetworkEvent.Context context = contextSupplier.get(); + context.enqueueWork(() -> { + ServerPlayer player = context.getSender(); + if (player != null) { + var playerExtension = PlayerExtension.getOrThrow(player); + if (playerExtension.isHandcuffed()) { + playerExtension.handcuffInteract(); + } + } + }); + context.setPacketHandled(true); + } +} diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/network/message/play/ParachuteSyncMessage.java b/crafting-dead-core/src/main/java/com/craftingdead/core/network/message/play/ParachuteSyncMessage.java new file mode 100644 index 000000000..829a07cb2 --- /dev/null +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/network/message/play/ParachuteSyncMessage.java @@ -0,0 +1,63 @@ +/* + * Crafting Dead + * Copyright (C) 2022 NexusNode LTD + * + * This Non-Commercial Software License Agreement (the "Agreement") is made between + * you (the "Licensee") and NEXUSNODE (BRAD HUNTER). (the "Licensor"). + * By installing or otherwise using Crafting Dead (the "Software"), you agree to be + * bound by the terms and conditions of this Agreement as may be revised from time + * to time at Licensor's sole discretion. + * + * If you do not agree to the terms and conditions of this Agreement do not download, + * copy, reproduce or otherwise use any of the source code available online at any time. + * + * https://github.com/nexusnode/crafting-dead/blob/1.18.x/LICENSE.txt + * + * https://craftingdead.net/terms.php + */ + +package com.craftingdead.core.network.message.play; + +import com.craftingdead.core.world.effect.ModMobEffects; +import java.util.function.Supplier; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraftforge.network.NetworkEvent; + +public class ParachuteSyncMessage { + + private final int entityId; + private final boolean hasParachute; + + public ParachuteSyncMessage(int entityId, boolean hasParachute) { + this.entityId = entityId; + this.hasParachute = hasParachute; + } + + public static void encode(ParachuteSyncMessage packet, FriendlyByteBuf buf) { + buf.writeInt(packet.entityId); + buf.writeBoolean(packet.hasParachute); + } + + public static ParachuteSyncMessage decode(FriendlyByteBuf buf) { + return new ParachuteSyncMessage(buf.readInt(), buf.readBoolean()); + } + + public static void handle(ParachuteSyncMessage packet, Supplier context) { + context.get().enqueueWork(() -> { + assert Minecraft.getInstance().level != null; + Entity entity = Minecraft.getInstance().level.getEntity(packet.entityId); + if (entity instanceof LivingEntity livingEntity) { + if (packet.hasParachute) { + livingEntity.addEffect(new MobEffectInstance(ModMobEffects.PARACHUTE.get())); + } else { + livingEntity.removeEffect(ModMobEffects.PARACHUTE.get()); + } + } + }); + context.get().setPacketHandled(true); + } +} diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/action/reload/MagazineReloadAction.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/action/reload/MagazineReloadAction.java index c019868a1..4997c0ed9 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/action/reload/MagazineReloadAction.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/action/reload/MagazineReloadAction.java @@ -60,7 +60,7 @@ public ActionType type() { @Override public boolean start(boolean simulate) { var result = this.findMagazine(this.performer()); - if (!result.isPresent()) { + if (this.performer.entity().isSprinting() || result.isEmpty()) { return false; } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/effect/ParachuteMobEffect.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/effect/ParachuteMobEffect.java index 021586afc..d8e884309 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/effect/ParachuteMobEffect.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/effect/ParachuteMobEffect.java @@ -18,11 +18,14 @@ package com.craftingdead.core.world.effect; +import com.craftingdead.core.network.NetworkChannel; +import com.craftingdead.core.network.message.play.ParachuteSyncMessage; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraftforge.common.ForgeMod; +import net.minecraftforge.network.PacketDistributor; public class ParachuteMobEffect extends MobEffect { @@ -35,10 +38,12 @@ public ParachuteMobEffect() { @Override public void applyEffectTick(LivingEntity livingEntity, int amplifier) { livingEntity.resetFallDistance(); - if (livingEntity.isOnGround()) { + if (livingEntity.isOnGround() || livingEntity.isInWater()) { livingEntity.removeEffect(ModMobEffects.PARACHUTE.get()); + syncParachuteEffect(livingEntity, false); return; } + syncParachuteEffect(livingEntity, true); super.applyEffectTick(livingEntity, amplifier); } @@ -51,4 +56,12 @@ public boolean isDurationEffectTick(int duration, int amplifier) { public boolean isInstantenous() { return false; } + + private void syncParachuteEffect(LivingEntity entity, boolean hasParachute) { + if (!entity.level.isClientSide()) { + NetworkChannel.PLAY.getSimpleChannel() + .send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> entity), + new ParachuteSyncMessage(entity.getId(), hasParachute)); + } + } } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtension.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtension.java index d9480c383..721052225 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtension.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtension.java @@ -18,6 +18,8 @@ package com.craftingdead.core.world.entity.extension; +import com.craftingdead.core.ServerConfig; +import net.minecraft.sounds.SoundEvents; import org.jetbrains.annotations.Nullable; import com.craftingdead.core.capability.CapabilityUtil; import com.craftingdead.core.world.item.equipment.Equipment; @@ -68,4 +70,14 @@ default boolean damageHandcuffs(int damage) { this.setHandcuffs(handcuffs); return false; } + + default void handcuffInteract() { + if (this.random().nextFloat() < ServerConfig.instance.handcuffDamageChance.get().floatValue() + && !this.damageHandcuffs(1) + && !this.entity().isSilent()) { + this.level().playSound(null, this.entity().getX(), this.entity().getY(), + this.entity().getZ(), SoundEvents.ITEM_BREAK, this.entity().getSoundSource(), 0.8F, + 0.8F + this.level().getRandom().nextFloat() * 0.4F); + } + } } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtensionImpl.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtensionImpl.java index 6362f7194..4443e7441 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtensionImpl.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/entity/extension/PlayerExtensionImpl.java @@ -18,9 +18,6 @@ package com.craftingdead.core.world.entity.extension; -import java.util.Collection; -import java.util.function.Consumer; -import org.apache.commons.lang3.mutable.MutableInt; import com.craftingdead.core.event.OpenEquipmentMenuEvent; import com.craftingdead.core.network.NetworkChannel; import com.craftingdead.core.network.SynchedData; @@ -28,6 +25,8 @@ import com.craftingdead.core.world.action.Action; import com.craftingdead.core.world.inventory.EquipmentMenu; import com.craftingdead.core.world.item.equipment.Equipment; +import java.util.Collection; +import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -37,7 +36,6 @@ import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; import net.minecraft.world.InteractionHand; import net.minecraft.world.SimpleMenuProvider; import net.minecraft.world.damagesource.DamageSource; @@ -51,12 +49,11 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.Event; +import org.apache.commons.lang3.mutable.MutableInt; final class PlayerExtensionImpl extends BaseLivingExtension implements PlayerExtension { - private static final float HANDCUFFS_DAMAGE_CHANCE = 0.1F; - private final SynchedData data = new SynchedData(); private static final EntityDataAccessor COMBAT_MODE_ENABLED = @@ -113,15 +110,7 @@ public boolean handleAttack(Entity target) { if (this.handlers.values().stream().anyMatch(e -> e.handleAttack(target))) { return true; } - - if (this.isHandcuffed()) { - if (!this.level().isClientSide()) { - this.handcuffInteract(HANDCUFFS_DAMAGE_CHANCE); - } - return true; - } - - return false; + return this.isHandcuffed(); } @Override @@ -129,15 +118,7 @@ public boolean handleInteract(InteractionHand hand, Entity target) { if (this.handlers.values().stream().anyMatch(e -> e.handleInteract(hand, target))) { return true; } - - if (this.isHandcuffed()) { - if (!this.level().isClientSide()) { - this.handcuffInteract(HANDCUFFS_DAMAGE_CHANCE); - } - return true; - } - - return false; + return this.isHandcuffed(); } @Override @@ -151,8 +132,6 @@ public boolean handleLeftClickBlock(BlockPos pos, Direction face, if (this.isHandcuffed()) { if (this.level().isClientSide()) { mineResult.accept(Event.Result.DENY); - } else { - this.handcuffInteract(HANDCUFFS_DAMAGE_CHANCE); } return true; } @@ -165,15 +144,7 @@ public boolean handleRightClickBlock(InteractionHand hand, BlockPos pos, Directi if (this.handlers.values().stream().anyMatch(e -> e.handleRightClickBlock(hand, pos, face))) { return true; } - - if (this.isHandcuffed()) { - if (!this.level().isClientSide()) { - this.handcuffInteract(HANDCUFFS_DAMAGE_CHANCE); - } - return true; - } - - return false; + return this.isHandcuffed(); } @Override @@ -181,31 +152,13 @@ public boolean handleRightClickItem(InteractionHand hand) { if (this.handlers.values().stream().anyMatch(e -> e.handleRightClickItem(hand))) { return true; } - - if (this.isHandcuffed()) { - if (!this.level().isClientSide()) { - this.handcuffInteract(HANDCUFFS_DAMAGE_CHANCE); - } - return true; - } - - return false; + return this.isHandcuffed(); } public boolean handleBlockBreak(BlockPos pos, BlockState block, MutableInt xp) { return this.handlers.values().stream().anyMatch(e -> e.handleBlockBreak(pos, block, xp)); } - private void handcuffInteract(float chance) { - if (this.random().nextFloat() < chance - && !this.damageHandcuffs(1) - && !this.entity().isSilent()) { - this.level().playSound(null, this.entity().getX(), this.entity().getY(), - this.entity().getZ(), SoundEvents.ITEM_BREAK, this.entity().getSoundSource(), 0.8F, - 0.8F + this.level().getRandom().nextFloat() * 0.4F); - } - } - @Override public boolean isCombatModeEnabled() { return !this.entity().isSpectator() diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ArbitraryTooltips.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ArbitraryTooltips.java index 8566f2bf0..7bc6e4042 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ArbitraryTooltips.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ArbitraryTooltips.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.function.Supplier; + import org.jetbrains.annotations.Nullable; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; @@ -36,16 +37,14 @@ */ public class ArbitraryTooltips { - private static final Multimap, TooltipFunction> toRegister = - ArrayListMultimap.create(); - private static final Multimap, TooltipFunction> functions = - ArrayListMultimap.create(); + private static final Multimap, TooltipFunction> toRegister = ArrayListMultimap.create(); + private static final Multimap, TooltipFunction> functions = ArrayListMultimap.create(); - public static void registerTooltip(Supplier item, Component tooltip) { + public static void registerTooltip(Supplier item, Component tooltip) { registerTooltip(item, (stack, level, flag) -> tooltip); } - public static void registerTooltip(Supplier item, TooltipFunction function) { + public static void registerTooltip(Supplier item, TooltipFunction function) { toRegister.put(item, function); } @@ -67,7 +66,6 @@ public static Collection getFunctions(Item item) { public interface TooltipFunction { @Nullable - Component createTooltip(ItemStack itemStack, @Nullable Level level, - TooltipFlag tooltipFlag); + Component createTooltip(ItemStack itemStack, @Nullable Level level, TooltipFlag tooltipFlag); } } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/HandcuffsItem.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/HandcuffsItem.java deleted file mode 100644 index 12e2186b9..000000000 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/HandcuffsItem.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Crafting Dead - * Copyright (C) 2022 NexusNode LTD - * - * This Non-Commercial Software License Agreement (the "Agreement") is made between - * you (the "Licensee") and NEXUSNODE (BRAD HUNTER). (the "Licensor"). - * By installing or otherwise using Crafting Dead (the "Software"), you agree to be - * bound by the terms and conditions of this Agreement as may be revised from time - * to time at Licensor's sole discretion. - * - * If you do not agree to the terms and conditions of this Agreement do not download, - * copy, reproduce or otherwise use any of the source code available online at any time. - * - * https://github.com/nexusnode/crafting-dead/blob/1.18.x/LICENSE.txt - * - * https://craftingdead.net/terms.php - */ - -package com.craftingdead.core.world.item; - -import java.util.List; -import org.jetbrains.annotations.Nullable; -import com.craftingdead.core.world.entity.extension.PlayerExtension; -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.Level; - -public class HandcuffsItem extends Item { - - public HandcuffsItem(Properties properties) { - super(properties); - } - - @Override - public InteractionResult interactLivingEntity(ItemStack itemStack, Player player, - LivingEntity livingEntity, InteractionHand hand) { - if (livingEntity instanceof ServerPlayer playerHit) { - if (playerHit.getAbilities().invulnerable) { - return InteractionResult.PASS; - } - - var extension = PlayerExtension.getOrThrow(playerHit); - if (!extension.isHandcuffed()) { - extension.setHandcuffs(itemStack.copy()); - playerHit.displayClientMessage( - new TranslatableComponent("handcuffs.handcuffed", player.getDisplayName()) - .withStyle(ChatFormatting.RED), - true); - itemStack.setCount(0); - } - } - return InteractionResult.sidedSuccess(player.getLevel().isClientSide()); - } - - @Override - public void appendHoverText(ItemStack item, @Nullable Level level, - List lines, TooltipFlag tooltipFlag) { - lines.add(new TranslatableComponent("handcuffs.information").withStyle(ChatFormatting.GRAY)); - } -} diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ModItems.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ModItems.java index 267797a82..ccf2f3cf7 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ModItems.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ModItems.java @@ -2030,5 +2030,8 @@ private static StorageItem gunBag() { ArbitraryTooltips.registerTooltip(SCUBA_CLOTHING, new TranslatableComponent("clothing_item.water_speed") .withStyle(ChatFormatting.GRAY)); + ArbitraryTooltips.registerTooltip(HANDCUFFS, + new TranslatableComponent("handcuffs.information") + .withStyle(ChatFormatting.GRAY)); } } diff --git a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ParachuteItem.java b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ParachuteItem.java index fc42909ca..9b0a75718 100644 --- a/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ParachuteItem.java +++ b/crafting-dead-core/src/main/java/com/craftingdead/core/world/item/ParachuteItem.java @@ -26,6 +26,7 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; public class ParachuteItem extends Item { @@ -34,13 +35,15 @@ public ParachuteItem(Properties properties) { } @Override - public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + public @NotNull InteractionResultHolder use(@NotNull Level level, Player player, @NotNull InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); - player.addEffect( - new MobEffectInstance(ModMobEffects.PARACHUTE.get(), 1200, 0, false, false)); - if (!player.getAbilities().invulnerable) { - itemstack.shrink(1); + if (!player.hasEffect(ModMobEffects.PARACHUTE.get())) { + player.addEffect(new MobEffectInstance(ModMobEffects.PARACHUTE.get(), 1200, 0, false, false)); + if (!player.getAbilities().invulnerable) { + itemstack.shrink(1); + } + return InteractionResultHolder.sidedSuccess(itemstack, level.isClientSide()); } - return InteractionResultHolder.sidedSuccess(itemstack, level.isClientSide()); + return InteractionResultHolder.fail(itemstack); } } diff --git a/crafting-dead-core/src/main/resources/assets/craftingdead/lang/en_us.json b/crafting-dead-core/src/main/resources/assets/craftingdead/lang/en_us.json index 7f25342ff..cf96ca8df 100644 --- a/crafting-dead-core/src/main/resources/assets/craftingdead/lang/en_us.json +++ b/crafting-dead-core/src/main/resources/assets/craftingdead/lang/en_us.json @@ -19,6 +19,12 @@ "options.craftingdead.client.crosshair": "Crosshair", "options.craftingdead.client.kill_sound": "Kill Sound", + "options.craftingdead.server.override_minecraft_inventory": "Override Minecraft Inventory", + "options.craftingdead.server.override_minecraft_hotbar": "Override Minecraft Hotbar", + "options.craftingdead.server.allow_offhand_swap": "Allow Offhand Swap", + "options.craftingdead.server.allow_toggle_perspective": "Allow Toggle Perspective", + "options.craftingdead.server.handcuff_damage_chance": "Handcuff Damage Chance", + "options.craftingdead.server.hit_marker_mode": "Hit Marker Mode", "options.craftingdead.server.kill_sound_enabled": "Kill Sound Enabled", "options.craftingdead.server.burstfire.enable":"Burst Fire Enabled", diff --git a/crafting-dead-core/src/main/resources/assets/craftingdead/textures/gui/container/widgets.png b/crafting-dead-core/src/main/resources/assets/craftingdead/textures/gui/container/widgets.png new file mode 100644 index 000000000..b9395fbd9 Binary files /dev/null and b/crafting-dead-core/src/main/resources/assets/craftingdead/textures/gui/container/widgets.png differ diff --git a/crafting-dead-core/src/main/resources/craftingdead.mixins.json b/crafting-dead-core/src/main/resources/craftingdead.mixins.json index e5087458c..87c166551 100644 --- a/crafting-dead-core/src/main/resources/craftingdead.mixins.json +++ b/crafting-dead-core/src/main/resources/craftingdead.mixins.json @@ -7,6 +7,7 @@ "mixins": [ "AbstractContainerMenuMixin", "EntityMixin", + "ItemEntityMixin", "ExplosionMixin", "LivingEntityMixin", "FriendlyByteBufMixin" @@ -17,6 +18,7 @@ "ItemRendererMixin", "ModelBakeryMixin", "MouseHandlerMixin", + "KeyboardHandlerMixin", "InventoryMixin", "PlayerRendererMixin" ],