From 791969a90a9ad774a9ab0229c245391099841740 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Tue, 12 Mar 2024 22:53:09 +0100 Subject: [PATCH 01/55] start working on the Security Sea Raft --- .../models/item/security_sea_raft.json | 6 ++++ .../transportation/security_sea_raft.json | 34 ++++++++++++++++++ .../recipes/security_sea_raft.json | 15 ++++++++ .../securitycraft/ClientHandler.java | 3 ++ .../geforcemods/securitycraft/SCContent.java | 10 ++++++ .../securitycraft/SCCreativeModeTabs.java | 1 + .../datagen/RecipeGenerator.java | 5 +++ .../securitycraft/entity/SecuritySeaRaft.java | 27 ++++++++++++++ .../items/SecuritySeaRaftItem.java | 31 ++++++++++++++++ .../renderers/SecuritySeaRaftRenderer.java | 26 ++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 1 + .../textures/entity/security_sea_raft.png | Bin 0 -> 3091 bytes .../textures/item/security_sea_raft.png | Bin 0 -> 348 bytes 13 files changed, 159 insertions(+) create mode 100644 src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json create mode 100644 src/generated/resources/data/securitycraft/recipes/security_sea_raft.json create mode 100644 src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java create mode 100644 src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java create mode 100644 src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_raft.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png diff --git a/src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json b/src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json new file mode 100644 index 0000000000..07f352def0 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/security_sea_raft" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json new file mode 100644 index 0000000000..f03c8feea1 --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:security_sea_raft" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:security_sea_raft" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json b/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json new file mode 100644 index 0000000000..68f65171c6 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:crafting_shapeless", + "category": "misc", + "ingredients": [ + { + "item": "securitycraft:keypad_chest" + }, + { + "item": "minecraft:bamboo_raft" + } + ], + "result": { + "item": "securitycraft:security_sea_raft" + } +} \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index 9bc700bd8e..023d004a96 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -61,6 +61,7 @@ import net.geforcemods.securitycraft.renderers.SecretHangingSignRenderer; import net.geforcemods.securitycraft.renderers.SecretSignRenderer; import net.geforcemods.securitycraft.renderers.SecurityCameraRenderer; +import net.geforcemods.securitycraft.renderers.SecuritySeaRaftRenderer; import net.geforcemods.securitycraft.renderers.SentryRenderer; import net.geforcemods.securitycraft.renderers.SonicSecuritySystemRenderer; import net.geforcemods.securitycraft.renderers.TrophySystemRenderer; @@ -107,6 +108,7 @@ import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.LecternRenderer; +import net.minecraft.client.renderer.entity.BoatRenderer; import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.client.renderer.item.ItemProperties; import net.minecraft.client.resources.model.BakedModel; @@ -414,6 +416,7 @@ public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderer event.registerEntityRenderer(SCContent.SECURITY_CAMERA_ENTITY.get(), NoopRenderer::new); event.registerEntityRenderer(SCContent.SENTRY_ENTITY.get(), SentryRenderer::new); event.registerEntityRenderer(SCContent.BULLET_ENTITY.get(), BulletRenderer::new); + event.registerEntityRenderer(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), SecuritySeaRaftRenderer::new); //normal renderers event.registerBlockEntityRenderer(SCContent.BLOCK_POCKET_MANAGER_BLOCK_ENTITY.get(), BlockPocketManagerRenderer::new); event.registerBlockEntityRenderer(SCContent.CLAYMORE_BLOCK_ENTITY.get(), ClaymoreRenderer::new); diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index d6b33107cb..7fc5def4c1 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -177,6 +177,7 @@ import net.geforcemods.securitycraft.commands.SingleGameProfileArgument; import net.geforcemods.securitycraft.entity.BouncingBetty; import net.geforcemods.securitycraft.entity.IMSBomb; +import net.geforcemods.securitycraft.entity.SecuritySeaRaft; import net.geforcemods.securitycraft.entity.camera.SecurityCamera; import net.geforcemods.securitycraft.entity.sentry.Bullet; import net.geforcemods.securitycraft.entity.sentry.Sentry; @@ -216,6 +217,7 @@ import net.geforcemods.securitycraft.items.ModuleItem; import net.geforcemods.securitycraft.items.PortableTunePlayerItem; import net.geforcemods.securitycraft.items.SCManualItem; +import net.geforcemods.securitycraft.items.SecuritySeaRaftItem; import net.geforcemods.securitycraft.items.SentryItem; import net.geforcemods.securitycraft.items.SentryRemoteAccessToolItem; import net.geforcemods.securitycraft.items.SonicSecuritySystemItem; @@ -249,6 +251,7 @@ import net.minecraft.network.syncher.EntityDataSerializer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.flag.FeatureFlag; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.BlockItem; @@ -2627,6 +2630,8 @@ public class SCContent { public static final DeferredItem SECRET_CRIMSON_HANGING_SIGN_ITEM = ITEMS.register("secret_crimson_hanging_sign", () -> new HangingSignItem(SCContent.SECRET_CRIMSON_HANGING_SIGN.get(), SCContent.SECRET_CRIMSON_WALL_HANGING_SIGN.get(), itemProp().stacksTo(16))); @HasManualPage(PageGroup.SECRET_HANGING_SIGNS) public static final DeferredItem SECRET_WARPED_HANGING_SIGN_ITEM = ITEMS.register("secret_warped_hanging_sign", () -> new HangingSignItem(SCContent.SECRET_WARPED_HANGING_SIGN.get(), SCContent.SECRET_WARPED_WALL_HANGING_SIGN.get(), itemProp().stacksTo(16))); + @HasManualPage + public static final DeferredItem SECURITY_SEA_RAFT_ITEM = ITEMS.register("security_sea_raft", () -> new SecuritySeaRaftItem(itemProp().stacksTo(1))); @HasManualPage(designedBy = "Henzoid") public static final DeferredItem SENTRY = ITEMS.register("sentry", () -> new SentryItem(itemProp())); public static final DeferredItem SONIC_SECURITY_SYSTEM_ITEM = ITEMS.register("sonic_security_system", () -> new SonicSecuritySystemItem(itemProp().stacksTo(1))); @@ -2917,6 +2922,11 @@ else if (state.is(BOUNCING_BETTY)) .setUpdateInterval(1) .setShouldReceiveVelocityUpdates(true) .build(SecurityCraft.MODID + ":bullet")); + public static final DeferredHolder, EntityType> SECURITY_SEA_RAFT_ENTITY = ENTITY_TYPES.register("security_sea_raft", + () -> EntityType.Builder.of(SecuritySeaRaft::new, MobCategory.MISC) + .sized(1.375F, 0.5625F) + .clientTrackingRange(10) + .build(SecurityCraft.MODID + ":security_sea_raft")); //@formatter:on //container types diff --git a/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java b/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java index c198961c2d..716a731a0c 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java +++ b/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java @@ -128,6 +128,7 @@ public class SCCreativeModeTabs { output.accept(new ItemStack(SCContent.FAKE_WATER_BUCKET.get())); output.accept(new ItemStack(SCContent.FAKE_LAVA_BUCKET.get())); output.accept(new ItemStack(SCContent.ADMIN_TOOL.get())); + output.accept(new ItemStack(SCContent.SECURITY_SEA_RAFT_ITEM.get())); output.acceptAll(STACKS_FOR_ITEM_GROUPS.get(SCItemGroup.TECHNICAL)); }).build()); //@formatter:off diff --git a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java index 7111dd0988..a2a6fd9779 100644 --- a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java +++ b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java @@ -668,6 +668,11 @@ protected final void buildRecipes(RecipeOutput recipeOutput) { .requires(SCContent.RETINAL_SCANNER.get()) .unlockedBy("has_reinforced_trapdoor", has(SCContent.REINFORCED_IRON_TRAPDOOR.get())) .save(recipeOutput); + ShapelessRecipeBuilder.shapeless(RecipeCategory.TRANSPORTATION, SCContent.SECURITY_SEA_RAFT_ITEM.get()) + .requires(SCContent.KEYPAD_CHEST.get()) + .requires(Items.BAMBOO_RAFT) + .unlockedBy("has_keypad_chest", has(SCContent.KEYPAD_CHEST.get())) + .save(recipeOutput); ShapelessRecipeBuilder.shapeless(RecipeCategory.TOOLS, SCContent.UNIVERSAL_OWNER_CHANGER.get()) .requires(SCContent.UNIVERSAL_BLOCK_MODIFIER.get()) .requires(Items.NAME_TAG) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java new file mode 100644 index 0000000000..783c406ec8 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -0,0 +1,27 @@ +package net.geforcemods.securitycraft.entity; + +import net.geforcemods.securitycraft.SCContent; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.ChestBoat; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.Level; + +public class SecuritySeaRaft extends ChestBoat { + public SecuritySeaRaft(EntityType type, Level level) { + super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); + } + + public SecuritySeaRaft(Level level, double x, double y, double z) { + super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); + setPos(x, y, z); + xo = y; + yo = y; + zo = z; + } + + @Override + public Item getDropItem() { + return SCContent.SECURITY_SEA_RAFT_ITEM.get(); + } +} diff --git a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java new file mode 100644 index 0000000000..f11abd542c --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java @@ -0,0 +1,31 @@ +package net.geforcemods.securitycraft.items; + +import net.geforcemods.securitycraft.entity.SecuritySeaRaft; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.ChestBoat; +import net.minecraft.world.item.BoatItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public class SecuritySeaRaftItem extends BoatItem { + public SecuritySeaRaftItem(Item.Properties properties) { + super(true, Boat.Type.BAMBOO, properties); + } + + @Override + public Boat getBoat(Level level, HitResult hitResult, ItemStack stack, Player player) { + Vec3 vec3 = hitResult.getLocation(); + SecuritySeaRaft raft = new SecuritySeaRaft(level, vec3.x, vec3.y, vec3.z); + + if (level instanceof ServerLevel serverLevel) + EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(raft); + + return raft; + } +} diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java new file mode 100644 index 0000000000..199aea4818 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java @@ -0,0 +1,26 @@ +package net.geforcemods.securitycraft.renderers; + +import com.mojang.datafixers.util.Pair; + +import net.geforcemods.securitycraft.SecurityCraft; +import net.minecraft.client.model.ChestRaftModel; +import net.minecraft.client.model.ListModel; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.entity.BoatRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.vehicle.Boat; + +public class SecuritySeaRaftRenderer extends BoatRenderer { + private final Pair> textureAndModel; + + public SecuritySeaRaftRenderer(EntityRendererProvider.Context ctx) { + super(ctx, true); + textureAndModel = new Pair<>(new ResourceLocation(SecurityCraft.MODID, "textures/entity/security_sea_raft.png"), new ChestRaftModel(ctx.bakeLayer(ModelLayers.createChestBoatModelName(Boat.Type.BAMBOO)))); + } + + @Override + public Pair> getModelWithLocation(Boat boat) { + return textureAndModel; + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 973cffba38..033936a51a 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -66,3 +66,4 @@ public net.minecraft.client.gui.screens.inventory.BookViewScreen forwardButton public net.minecraft.client.gui.screens.inventory.BookViewScreen backButton public net.minecraft.client.gui.screens.inventory.BookViewScreen updateButtonVisibility()V public net.minecraft.server.commands.FillCommand ERROR_AREA_TOO_LARGE +public net.minecraft.world.item.BoatItem getBoat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/phys/HitResult;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/player/Player;)Lnet/minecraft/world/entity/vehicle/Boat; \ No newline at end of file diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_raft.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_raft.png new file mode 100644 index 0000000000000000000000000000000000000000..7e39dc083dfca7ce1ab15be8ba36abc7cdd3b79e GIT binary patch literal 3091 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 G81)~-HxPON literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png b/src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png new file mode 100644 index 0000000000000000000000000000000000000000..a3569c21e89746d71eee4c6631676f660d7e0d08 GIT binary patch literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|;0G|-og$oy6yLRo_vuB?^eJV{g>FDUVc_?nytXYvBx`$SKOlq*cbLWnU zmgM6zSutta4<0;tf3wccUDn-71!%xA1{P5uB~lXP7yKU+F!=jF7X-?47I;J!GcfQS z24TkI`72U@f}Nf&jv*Ssq316OF)4De24pc_>=63@KYsU)XycCx_l%8{x0GGHT6mlN z?z?!$^%V|3nvDOkDAYT7_j$XBFl7iBPgIk2a+VXQkE-@qVYG4IwmoY-k1%`@%B)Z} zv(&8Se5R1qu+=qcL%&>@z3xk!*lM4xQHsA4=DjpL&Z*6wzV7L&!{=4C7u~sQ?>d{S UCvvr17tm1*p00i_>zopr03tt%hyVZp literal 0 HcmV?d00001 From e5cd7c795ea77b53a5a67ad6101f9c644320d3a6 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Wed, 13 Mar 2024 18:04:15 +0100 Subject: [PATCH 02/55] formatting and import fixes --- .../securitycraft/ClientHandler.java | 1 - .../geforcemods/securitycraft/SCContent.java | 1 - .../securitycraft/entity/SecuritySeaRaft.java | 28 +++++++++---------- .../items/SecuritySeaRaftItem.java | 23 ++++++++------- .../renderers/SecuritySeaRaftRenderer.java | 18 ++++++------ 5 files changed, 34 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index 023d004a96..a54f0b25be 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -108,7 +108,6 @@ import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.LecternRenderer; -import net.minecraft.client.renderer.entity.BoatRenderer; import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.client.renderer.item.ItemProperties; import net.minecraft.client.resources.model.BakedModel; diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index 7fc5def4c1..06c302fcbd 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -251,7 +251,6 @@ import net.minecraft.network.syncher.EntityDataSerializer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.flag.FeatureFlag; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.BlockItem; diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 783c406ec8..8208869ac9 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -8,20 +8,20 @@ import net.minecraft.world.level.Level; public class SecuritySeaRaft extends ChestBoat { - public SecuritySeaRaft(EntityType type, Level level) { - super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); - } + public SecuritySeaRaft(EntityType type, Level level) { + super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); + } - public SecuritySeaRaft(Level level, double x, double y, double z) { - super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); - setPos(x, y, z); - xo = y; - yo = y; - zo = z; - } + public SecuritySeaRaft(Level level, double x, double y, double z) { + super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); + setPos(x, y, z); + xo = y; + yo = y; + zo = z; + } - @Override - public Item getDropItem() { - return SCContent.SECURITY_SEA_RAFT_ITEM.get(); - } + @Override + public Item getDropItem() { + return SCContent.SECURITY_SEA_RAFT_ITEM.get(); + } } diff --git a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java index f11abd542c..938a2d5196 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java @@ -5,7 +5,6 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.Boat; -import net.minecraft.world.entity.vehicle.ChestBoat; import net.minecraft.world.item.BoatItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -14,18 +13,18 @@ import net.minecraft.world.phys.Vec3; public class SecuritySeaRaftItem extends BoatItem { - public SecuritySeaRaftItem(Item.Properties properties) { - super(true, Boat.Type.BAMBOO, properties); - } + public SecuritySeaRaftItem(Item.Properties properties) { + super(true, Boat.Type.BAMBOO, properties); + } - @Override - public Boat getBoat(Level level, HitResult hitResult, ItemStack stack, Player player) { - Vec3 vec3 = hitResult.getLocation(); - SecuritySeaRaft raft = new SecuritySeaRaft(level, vec3.x, vec3.y, vec3.z); + @Override + public Boat getBoat(Level level, HitResult hitResult, ItemStack stack, Player player) { + Vec3 vec3 = hitResult.getLocation(); + SecuritySeaRaft raft = new SecuritySeaRaft(level, vec3.x, vec3.y, vec3.z); - if (level instanceof ServerLevel serverLevel) - EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(raft); + if (level instanceof ServerLevel serverLevel) + EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(raft); - return raft; - } + return raft; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java index 199aea4818..4e9a52e94f 100644 --- a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java +++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java @@ -12,15 +12,15 @@ import net.minecraft.world.entity.vehicle.Boat; public class SecuritySeaRaftRenderer extends BoatRenderer { - private final Pair> textureAndModel; + private final Pair> textureAndModel; - public SecuritySeaRaftRenderer(EntityRendererProvider.Context ctx) { - super(ctx, true); - textureAndModel = new Pair<>(new ResourceLocation(SecurityCraft.MODID, "textures/entity/security_sea_raft.png"), new ChestRaftModel(ctx.bakeLayer(ModelLayers.createChestBoatModelName(Boat.Type.BAMBOO)))); - } + public SecuritySeaRaftRenderer(EntityRendererProvider.Context ctx) { + super(ctx, true); + textureAndModel = new Pair<>(new ResourceLocation(SecurityCraft.MODID, "textures/entity/security_sea_raft.png"), new ChestRaftModel(ctx.bakeLayer(ModelLayers.createChestBoatModelName(Boat.Type.BAMBOO)))); + } - @Override - public Pair> getModelWithLocation(Boat boat) { - return textureAndModel; - } + @Override + public Pair> getModelWithLocation(Boat boat) { + return textureAndModel; + } } From c77156912079853a5c700dc1aa7f35034b70c29a Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Fri, 15 Mar 2024 16:57:32 +0100 Subject: [PATCH 03/55] make raft ownable --- .../compat/top/TOPDataProvider.java | 5 +- .../compat/waila/JadeDataProvider.java | 8 ++- .../compat/waila/WTHITDataProvider.java | 8 +-- .../securitycraft/entity/SecuritySeaRaft.java | 63 ++++++++++++++++++- .../items/SecuritySeaRaftItem.java | 1 + 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/compat/top/TOPDataProvider.java b/src/main/java/net/geforcemods/securitycraft/compat/top/TOPDataProvider.java index 9321687fe5..64a9a7382d 100644 --- a/src/main/java/net/geforcemods/securitycraft/compat/top/TOPDataProvider.java +++ b/src/main/java/net/geforcemods/securitycraft/compat/top/TOPDataProvider.java @@ -111,11 +111,12 @@ public String getID() { @Override public void addProbeEntityInfo(ProbeMode probeMode, IProbeInfo probeInfo, Player player, Level level, Entity entity, IProbeHitEntityData data) { + if (entity instanceof IOwnable ownable) + probeInfo.mcText(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(ownable.getOwner())).withStyle(ChatFormatting.GRAY)); + if (entity instanceof Sentry sentry) { SentryMode mode = sentry.getMode(); - probeInfo.mcText(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(sentry.getOwner())).withStyle(ChatFormatting.GRAY)); - if (!sentry.getAllowlistModule().isEmpty() || !sentry.getDisguiseModule().isEmpty() || sentry.hasSpeedModule()) { probeInfo.mcText(EQUIPPED); diff --git a/src/main/java/net/geforcemods/securitycraft/compat/waila/JadeDataProvider.java b/src/main/java/net/geforcemods/securitycraft/compat/waila/JadeDataProvider.java index 138e72e9fe..1b9809b03f 100644 --- a/src/main/java/net/geforcemods/securitycraft/compat/waila/JadeDataProvider.java +++ b/src/main/java/net/geforcemods/securitycraft/compat/waila/JadeDataProvider.java @@ -10,6 +10,7 @@ import net.geforcemods.securitycraft.blocks.FakeLavaBlock; import net.geforcemods.securitycraft.blocks.FakeWaterBlock; import net.geforcemods.securitycraft.compat.IOverlayDisplay; +import net.geforcemods.securitycraft.entity.SecuritySeaRaft; import net.geforcemods.securitycraft.entity.sentry.Sentry; import net.geforcemods.securitycraft.entity.sentry.Sentry.SentryMode; import net.geforcemods.securitycraft.misc.ModuleType; @@ -50,6 +51,7 @@ public void registerClient(IWailaClientRegistration registration) { registration.registerBlockComponent(SECURITYCRAFT_INFO, Block.class); registration.registerEntityComponent(SECURITYCRAFT_INFO, Sentry.class); + registration.registerEntityComponent(SECURITYCRAFT_INFO, SecuritySeaRaft.class); registration.addBeforeRenderCallback((tooltip, rect, guiGraphics, accessor) -> ClientHandler.isPlayerMountedOnCamera()); registration.addRayTraceCallback((hit, accessor, original) -> { @@ -121,12 +123,12 @@ public void appendTooltip(ITooltip tooltip, BlockAccessor data, IPluginConfig co public void appendTooltip(ITooltip tooltip, EntityAccessor data, IPluginConfig config) { Entity entity = data.getEntity(); + if (entity instanceof IOwnable ownable && config.get(SHOW_OWNER)) + tooltip.add(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(ownable.getOwner()))); + if (entity instanceof Sentry sentry) { SentryMode mode = sentry.getMode(); - if (config.get(SHOW_OWNER)) - tooltip.add(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(sentry.getOwner()))); - if (config.get(SHOW_MODULES) && sentry.isOwnedBy(data.getPlayer()) && (!sentry.getAllowlistModule().isEmpty() || !sentry.getDisguiseModule().isEmpty() || sentry.hasSpeedModule())) { tooltip.add(EQUIPPED); diff --git a/src/main/java/net/geforcemods/securitycraft/compat/waila/WTHITDataProvider.java b/src/main/java/net/geforcemods/securitycraft/compat/waila/WTHITDataProvider.java index a616719001..236fefa3c0 100644 --- a/src/main/java/net/geforcemods/securitycraft/compat/waila/WTHITDataProvider.java +++ b/src/main/java/net/geforcemods/securitycraft/compat/waila/WTHITDataProvider.java @@ -52,7 +52,7 @@ public void register(IRegistrar registrar) { registrar.addComponent((IBlockComponentProvider) INSTANCE, TooltipPosition.BODY, IOwnable.class); registrar.addComponent((IBlockComponentProvider) INSTANCE, TooltipPosition.TAIL, IOverlayDisplay.class); registrar.addIcon((IBlockComponentProvider) INSTANCE, IOverlayDisplay.class); - registrar.addComponent((IEntityComponentProvider) INSTANCE, TooltipPosition.BODY, Sentry.class); + registrar.addComponent((IEntityComponentProvider) INSTANCE, TooltipPosition.BODY, IOwnable.class); } @Override @@ -129,12 +129,12 @@ public void appendTail(ITooltip tooltip, IBlockAccessor accessor, IPluginConfig public void appendBody(ITooltip tooltip, IEntityAccessor data, IPluginConfig config) { Entity entity = data.getEntity(); + if (entity instanceof IOwnable ownable && config.getBoolean(SHOW_OWNER)) + tooltip.addLine(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(ownable.getOwner()))); + if (entity instanceof Sentry sentry) { SentryMode mode = sentry.getMode(); - if (config.getBoolean(SHOW_OWNER)) - tooltip.addLine(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(sentry.getOwner()))); - if (config.getBoolean(SHOW_MODULES) && sentry.isOwnedBy(data.getPlayer()) && (!sentry.getAllowlistModule().isEmpty() || !sentry.getDisguiseModule().isEmpty() || sentry.hasSpeedModule())) { tooltip.addLine(EQUIPPED); diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 8208869ac9..08e6b9c1f5 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -1,13 +1,26 @@ package net.geforcemods.securitycraft.entity; import net.geforcemods.securitycraft.SCContent; +import net.geforcemods.securitycraft.api.IOwnable; +import net.geforcemods.securitycraft.api.Owner; +import net.geforcemods.securitycraft.entity.sentry.Sentry; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.entity.vehicle.ChestBoat; import net.minecraft.world.item.Item; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +public class SecuritySeaRaft extends ChestBoat implements IOwnable { + private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(Sentry.class, Owner.getSerializer()); -public class SecuritySeaRaft extends ChestBoat { public SecuritySeaRaft(EntityType type, Level level) { super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); } @@ -20,6 +33,54 @@ public SecuritySeaRaft(Level level, double x, double y, double z) { zo = z; } + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + entityData.define(OWNER, new Owner()); + } + + @Override + public boolean hurt(DamageSource source, float amount) { + Entity entity = source.getEntity(); + + if (!(entity instanceof Player player) || isOwnedBy(player)) + return super.hurt(source, amount); + else + return false; + } + + @Override + protected void addAdditionalSaveData(CompoundTag tag) { + CompoundTag ownerTag = new CompoundTag(); + + super.addAdditionalSaveData(tag); + getOwner().save(ownerTag, needsValidation()); + tag.put("owner", ownerTag); + } + + @Override + protected void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); + entityData.set(OWNER, Owner.fromCompound(tag.getCompound("owner"))); + } + + public void setOwner(Player player) { + setOwner(player.getGameProfile().getId().toString(), player.getName().getString()); + } + + @Override + public void setOwner(String uuid, String name) { + entityData.set(OWNER, new Owner(name, uuid)); + } + + @Override + public Owner getOwner() { + return entityData.get(OWNER); + } + + @Override + public void onOwnerChanged(BlockState state, Level level, BlockPos pos, Player player) {} + @Override public Item getDropItem() { return SCContent.SECURITY_SEA_RAFT_ITEM.get(); diff --git a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java index 938a2d5196..43597a0586 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java @@ -25,6 +25,7 @@ public Boat getBoat(Level level, HitResult hitResult, ItemStack stack, Player pl if (level instanceof ServerLevel serverLevel) EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(raft); + raft.setOwner(player); return raft; } } From 0accbbc6f8939dcabffb68b54dc05a17b572cb98 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Fri, 15 Mar 2024 18:22:58 +0100 Subject: [PATCH 04/55] add ability to set a passcode --- .../securitycraft/ClientHandler.java | 8 +- .../securitycraft/api/IPasscodeProtected.java | 8 +- .../securitycraft/entity/SecuritySeaRaft.java | 79 ++++++++++++++++++- .../network/client/OpenScreen.java | 23 +++++- .../network/server/SetPasscode.java | 47 +++++++++-- .../screen/SetPasscodeScreen.java | 14 +++- 6 files changed, 160 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index a54f0b25be..1f4615e596 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -16,6 +16,7 @@ import net.geforcemods.securitycraft.api.IExplosive; import net.geforcemods.securitycraft.api.ILockable; import net.geforcemods.securitycraft.api.IOwnable; +import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.blockentities.AlarmBlockEntity; import net.geforcemods.securitycraft.blockentities.InventoryScannerBlockEntity; import net.geforcemods.securitycraft.blockentities.LaserBlockBlockEntity; @@ -121,6 +122,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Nameable; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.DyeableLeatherItem; @@ -726,7 +728,11 @@ public static void displayCheckPasscodeScreen(BlockEntity be) { public static void displaySetPasscodeScreen(BlockEntity be) { Component displayName = be instanceof Nameable nameable ? nameable.getDisplayName() : Component.translatable(be.getBlockState().getBlock().getDescriptionId()); - Minecraft.getInstance().setScreen(new SetPasscodeScreen(be, displayName)); + Minecraft.getInstance().setScreen(new SetPasscodeScreen((IPasscodeProtected) be, displayName)); + } + + public static void displaySetPasscodeScreen(Entity entity) { + Minecraft.getInstance().setScreen(new SetPasscodeScreen((IPasscodeProtected) entity, entity.getDisplayName())); } public static void displaySSSItemScreen(ItemStack stack) { diff --git a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java index dd029a4bf8..2a6d3de842 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java +++ b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java @@ -24,6 +24,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; +// TODO: Remove mentions of "block entity" and generalize /** * Implementing this interface designates a block entity as being passcode-protected. Implementing this allows you to use * {@link SetPasscodeScreen} and {@link CheckPasscodeScreen} to easily set your block's passcode. Extends @@ -59,7 +60,7 @@ default boolean verifyPasscodeSet(Level level, BlockPos pos, IOwnable ownable, P return true; if (ownable.isOwnedBy(player)) - PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.SET_PASSCODE, pos)); + openSetPasscodeScreen((ServerPlayer) player, pos); else PlayerUtils.sendMessageToPlayer(player, Component.literal("SecurityCraft"), Utils.localize("messages.securitycraft:passcodeProtected.notSetUp"), ChatFormatting.DARK_RED); } @@ -67,6 +68,11 @@ default boolean verifyPasscodeSet(Level level, BlockPos pos, IOwnable ownable, P return false; } + //TODO: Javadoc + default void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { + PacketDistributor.PLAYER.with(player).send(new OpenScreen(DataType.SET_PASSCODE, pos)); + } + @Override default boolean shouldAttemptCodebreak(BlockState state, Player player) { if (getPasscode() == null) { diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 08e6b9c1f5..883d5cb0b8 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -1,13 +1,21 @@ package net.geforcemods.securitycraft.entity; +import java.util.UUID; + import net.geforcemods.securitycraft.SCContent; import net.geforcemods.securitycraft.api.IOwnable; +import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.api.Owner; import net.geforcemods.securitycraft.entity.sentry.Sentry; +import net.geforcemods.securitycraft.network.client.OpenScreen; +import net.geforcemods.securitycraft.network.client.OpenScreen.DataType; +import net.geforcemods.securitycraft.util.PasscodeUtils; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -17,9 +25,12 @@ import net.minecraft.world.item.Item; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.network.PacketDistributor; -public class SecuritySeaRaft extends ChestBoat implements IOwnable { +public class SecuritySeaRaft extends ChestBoat implements IOwnable, IPasscodeProtected { private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(Sentry.class, Owner.getSerializer()); + private byte[] passcode; + private UUID saltKey; public SecuritySeaRaft(EntityType type, Level level) { super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); @@ -39,6 +50,28 @@ protected void defineSynchedData() { entityData.define(OWNER, new Owner()); } + @Override + public InteractionResult interactWithContainerVehicle(Player player) { + Level level = level(); + BlockPos pos = blockPosition(); + + //TODO: Proper codebreaker support + if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player) && !player.isHolding(SCContent.CODEBREAKER.get())) + openPasscodeGUI(level, pos, player); + + return !level.isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; + } + + @Override + public void openCustomInventoryScreen(Player player) { + interactWithContainerVehicle(player); + } + + @Override + public void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { + PacketDistributor.PLAYER.with(player).send(new OpenScreen(DataType.SET_PASSCODE_FOR_ENTITY, getId())); + } + @Override public boolean hurt(DamageSource source, float amount) { Entity entity = source.getEntity(); @@ -56,12 +89,20 @@ protected void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); getOwner().save(ownerTag, needsValidation()); tag.put("owner", ownerTag); + + if (saltKey != null) + tag.putUUID("saltKey", saltKey); + + if (passcode != null) + tag.putString("passcode", PasscodeUtils.bytesToString(passcode)); } @Override protected void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); entityData.set(OWNER, Owner.fromCompound(tag.getCompound("owner"))); + loadSaltKey(tag); + loadPasscode(tag); } public void setOwner(Player player) { @@ -81,6 +122,42 @@ public Owner getOwner() { @Override public void onOwnerChanged(BlockState state, Level level, BlockPos pos, Player player) {} + @Override + public void activate(Player player) {} + + @Override + public byte[] getPasscode() { + return passcode == null || passcode.length == 0 ? null : passcode; + } + + @Override + public void setPasscode(byte[] passcode) { + this.passcode = passcode; + } + + @Override + public UUID getSaltKey() { + return saltKey; + } + + @Override + public void setSaltKey(UUID saltKey) { + this.saltKey = saltKey; + } + + @Override + public void startCooldown() {} + + @Override + public boolean isOnCooldown() { + return false; + } + + @Override + public long getCooldownEnd() { + return 0; + } + @Override public Item getDropItem() { return SCContent.SECURITY_SEA_RAFT_ITEM.get(); diff --git a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java index 6179728185..2ae6c44aa0 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java @@ -16,6 +16,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.handling.PlayPayloadContext; @@ -25,6 +26,7 @@ public class OpenScreen implements CustomPacketPayload { private DataType dataType; private BlockPos pos; private CompoundTag tag; + private int entityId; public OpenScreen() {} @@ -45,14 +47,20 @@ public OpenScreen(DataType dataType, CompoundTag tag) { this.tag = tag; } + public OpenScreen(DataType dataType, int entityId) { + this.dataType = dataType; + this.entityId = entityId; + } + public OpenScreen(FriendlyByteBuf buf) { dataType = buf.readEnum(DataType.class); if (dataType.needsPosition) pos = buf.readBlockPos(); - - if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) + else if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) tag = buf.readNbt(); + else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY) + entityId = buf.readVarInt(); } @Override @@ -61,9 +69,10 @@ public void write(FriendlyByteBuf buf) { if (dataType.needsPosition) buf.writeBlockPos(pos); - - if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) + else if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) buf.writeNbt(tag); + else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY) + buf.writeVarInt(entityId); } @Override @@ -115,6 +124,11 @@ public void handle(PlayPayloadContext ctx) { if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) ClientHandler.displaySetPasscodeScreen((BlockEntity) be); + break; + case SET_PASSCODE_FOR_ENTITY: + if (Minecraft.getInstance().level.getEntity(entityId) instanceof IPasscodeProtected be) + ClientHandler.displaySetPasscodeScreen((Entity) be); + break; case SONIC_SECURITY_SYSTEM: if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof SonicSecuritySystemBlockEntity sss) @@ -139,6 +153,7 @@ public enum DataType { SENTRY_REMOTE_ACCESS_TOOL(false), SET_BRIEFCASE_PASSCODE(false), SET_PASSCODE(true), + SET_PASSCODE_FOR_ENTITY(false), SONIC_SECURITY_SYSTEM(true), UNIVERSAL_KEY_CHANGER(true); diff --git a/src/main/java/net/geforcemods/securitycraft/network/server/SetPasscode.java b/src/main/java/net/geforcemods/securitycraft/network/server/SetPasscode.java index b227bc849d..1d7ad4dc47 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/server/SetPasscode.java +++ b/src/main/java/net/geforcemods/securitycraft/network/server/SetPasscode.java @@ -19,24 +19,45 @@ public class SetPasscode implements CustomPacketPayload { public static final ResourceLocation ID = new ResourceLocation(SecurityCraft.MODID, "set_passcode"); + private boolean isEntity; private BlockPos pos; + private int entityId; private String passcode; public SetPasscode() {} public SetPasscode(BlockPos pos, String code) { + this.isEntity = false; this.pos = pos; passcode = PasscodeUtils.hashPasscodeWithoutSalt(code); } + public SetPasscode(int entityId, String code) { + this.isEntity = true; + this.entityId = entityId; + passcode = PasscodeUtils.hashPasscodeWithoutSalt(code); + } + public SetPasscode(FriendlyByteBuf buf) { - pos = buf.readBlockPos(); + isEntity = buf.readBoolean(); + + if (isEntity) + entityId = buf.readVarInt(); + else + pos = buf.readBlockPos(); + passcode = buf.readUtf(Integer.MAX_VALUE / 4); } @Override public void write(FriendlyByteBuf buf) { - buf.writeBlockPos(pos); + buf.writeBoolean(isEntity); + + if (isEntity) + buf.writeVarInt(entityId); + else + buf.writeBlockPos(pos); + buf.writeUtf(passcode); } @@ -48,14 +69,24 @@ public ResourceLocation id() { public void handle(PlayPayloadContext ctx) { Player player = ctx.player().orElseThrow(); Level level = player.level(); + IPasscodeProtected passcodeProtected = null; - if (level.getBlockEntity(pos) instanceof IPasscodeProtected be && (!(be instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { - be.hashAndSetPasscode(passcode); + if (isEntity) { + if (level.getEntity(entityId) instanceof IPasscodeProtected pp) + passcodeProtected = pp; + } + else if (level.getBlockEntity(pos) instanceof IPasscodeProtected pp) + passcodeProtected = pp; - if (be instanceof KeypadChestBlockEntity chestBe) - checkAndUpdateAdjacentChest(chestBe, level, pos, passcode, be.getSalt()); - else if (be instanceof KeypadDoorBlockEntity doorBe) - checkAndUpdateAdjacentDoor(doorBe, level, passcode, be.getSalt()); + if (passcodeProtected != null && (!(passcodeProtected instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { + passcodeProtected.hashAndSetPasscode(passcode); + + if (!isEntity) { + if (passcodeProtected instanceof KeypadChestBlockEntity chestBe) + checkAndUpdateAdjacentChest(chestBe, level, pos, passcode, passcodeProtected.getSalt()); + else if (passcodeProtected instanceof KeypadDoorBlockEntity doorBe) + checkAndUpdateAdjacentDoor(doorBe, level, passcode, passcodeProtected.getSalt()); + } } } diff --git a/src/main/java/net/geforcemods/securitycraft/screen/SetPasscodeScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/SetPasscodeScreen.java index 7709cc3698..999f9b8c5c 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/SetPasscodeScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/SetPasscodeScreen.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.platform.InputConstants; +import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.network.server.SetPasscode; import net.geforcemods.securitycraft.util.Utils; import net.minecraft.client.Minecraft; @@ -12,6 +13,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.PacketDistributor; @@ -21,14 +23,14 @@ public class SetPasscodeScreen extends Screen { private int imageHeight = 166; private int leftPos; private int topPos; - private BlockEntity be; + private IPasscodeProtected passcodeProtected; private Component setup; private MutableComponent combined; private EditBox keycodeTextbox; - public SetPasscodeScreen(BlockEntity be, Component title) { + public SetPasscodeScreen(IPasscodeProtected passcodeProtected, Component title) { super(title); - this.be = be; + this.passcodeProtected = passcodeProtected; setup = Utils.localize("gui.securitycraft:passcode.setup"); combined = title.plainCopy().append(Component.literal(" ")).append(setup); } @@ -85,7 +87,11 @@ public boolean isPauseScreen() { } private void saveAndContinueButtonClicked(Button button) { - PacketDistributor.SERVER.noArg().send(new SetPasscode(be.getBlockPos(), keycodeTextbox.getValue())); + if (passcodeProtected instanceof BlockEntity be) + PacketDistributor.SERVER.noArg().send(new SetPasscode(be.getBlockPos(), keycodeTextbox.getValue())); + else if (passcodeProtected instanceof Entity entity) + PacketDistributor.SERVER.noArg().send(new SetPasscode(entity.getId(), keycodeTextbox.getValue())); + Minecraft.getInstance().player.closeContainer(); } } From 29f12eee501163706a4c3c0e8ff5cf781a231768 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Fri, 15 Mar 2024 20:06:42 +0100 Subject: [PATCH 05/55] rename existing OpenScreen data types for consistency --- .../securitycraft/items/BriefcaseItem.java | 2 +- .../network/client/OpenScreen.java | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/items/BriefcaseItem.java b/src/main/java/net/geforcemods/securitycraft/items/BriefcaseItem.java index 0fdffe4e80..2663895f11 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/BriefcaseItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/BriefcaseItem.java @@ -60,7 +60,7 @@ public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantmen private void handle(ItemStack stack, Level level, Player player) { if (!level.isClientSide) - PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(stack.getOrCreateTag().contains("passcode") ? OpenScreen.DataType.CHECK_BRIEFCASE_PASSCODE : OpenScreen.DataType.SET_BRIEFCASE_PASSCODE)); + PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(stack.getOrCreateTag().contains("passcode") ? OpenScreen.DataType.CHECK_PASSCODE_FOR_BRIEFCASE : OpenScreen.DataType.SET_PASSCODE_FOR_BRIEFCASE)); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java index 2ae6c44aa0..2cf02ab105 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java @@ -87,17 +87,17 @@ public void handle(PlayPayloadContext ctx) { ClientHandler.displayAlarmScreen(be); break; - case CHECK_BRIEFCASE_PASSCODE: + case CHECK_PASSCODE: + if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) + ClientHandler.displayCheckPasscodeScreen((BlockEntity) be); + + break; + case CHECK_PASSCODE_FOR_BRIEFCASE: ItemStack briefcaseStack = PlayerUtils.getItemStackFromAnyHand(ClientHandler.getClientPlayer(), SCContent.BRIEFCASE.get()); if (!briefcaseStack.isEmpty()) ClientHandler.displayBriefcasePasscodeScreen(briefcaseStack.getHoverName()); - break; - case CHECK_PASSCODE: - if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) - ClientHandler.displayCheckPasscodeScreen((BlockEntity) be); - break; case RIFT_STABILIZER: if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof RiftStabilizerBlockEntity riftStabilizer) @@ -113,17 +113,17 @@ public void handle(PlayPayloadContext ctx) { } break; - case SET_BRIEFCASE_PASSCODE: + case SET_PASSCODE: + if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) + ClientHandler.displaySetPasscodeScreen((BlockEntity) be); + + break; + case SET_PASSCODE_FOR_BRIEFCASE: ItemStack briefcase = PlayerUtils.getItemStackFromAnyHand(ClientHandler.getClientPlayer(), SCContent.BRIEFCASE.get()); if (!briefcase.isEmpty()) ClientHandler.displayBriefcaseSetupScreen(briefcase.getHoverName().plainCopy().append(Component.literal(" ")).append(Utils.localize("gui.securitycraft:passcode.setup"))); - break; - case SET_PASSCODE: - if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) - ClientHandler.displaySetPasscodeScreen((BlockEntity) be); - break; case SET_PASSCODE_FOR_ENTITY: if (Minecraft.getInstance().level.getEntity(entityId) instanceof IPasscodeProtected be) @@ -147,12 +147,12 @@ public void handle(PlayPayloadContext ctx) { public enum DataType { ALARM(true), - CHECK_BRIEFCASE_PASSCODE(false), CHECK_PASSCODE(true), + CHECK_PASSCODE_FOR_BRIEFCASE(false), RIFT_STABILIZER(true), SENTRY_REMOTE_ACCESS_TOOL(false), - SET_BRIEFCASE_PASSCODE(false), SET_PASSCODE(true), + SET_PASSCODE_FOR_BRIEFCASE(false), SET_PASSCODE_FOR_ENTITY(false), SONIC_SECURITY_SYSTEM(true), UNIVERSAL_KEY_CHANGER(true); From cb788360185f37f1b7375eca68f4204f73c4e8fb Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Fri, 15 Mar 2024 22:18:46 +0100 Subject: [PATCH 06/55] make it possible to open the inventory by entering the correct code --- .../securitycraft/ClientHandler.java | 6 ++- .../securitycraft/entity/SecuritySeaRaft.java | 11 +++- .../network/client/OpenScreen.java | 10 +++- .../network/server/CheckPasscode.java | 50 ++++++++++++++++--- .../screen/CheckPasscodeScreen.java | 28 ++++++----- 5 files changed, 80 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index 1f4615e596..cbea41bd22 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -722,7 +722,11 @@ public static void displayUniversalKeyChangerScreen(BlockEntity be) { public static void displayCheckPasscodeScreen(BlockEntity be) { Component displayName = be instanceof Nameable nameable ? nameable.getDisplayName() : Component.translatable(be.getBlockState().getBlock().getDescriptionId()); - Minecraft.getInstance().setScreen(new CheckPasscodeScreen(be, displayName)); + Minecraft.getInstance().setScreen(new CheckPasscodeScreen((IPasscodeProtected) be, displayName)); + } + + public static void displayCheckPasscodeScreen(Entity entity) { + Minecraft.getInstance().setScreen(new CheckPasscodeScreen((IPasscodeProtected) entity, entity.getDisplayName())); } public static void displaySetPasscodeScreen(BlockEntity be) { diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 883d5cb0b8..a7d421910f 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -67,6 +67,12 @@ public void openCustomInventoryScreen(Player player) { interactWithContainerVehicle(player); } + @Override + public void openPasscodeGUI(Level level, BlockPos pos, Player player) { + if (!level.isClientSide && getPasscode() != null) + PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHECK_PASSCODE_FOR_ENTITY, getId())); + } + @Override public void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { PacketDistributor.PLAYER.with(player).send(new OpenScreen(DataType.SET_PASSCODE_FOR_ENTITY, getId())); @@ -123,7 +129,10 @@ public Owner getOwner() { public void onOwnerChanged(BlockState state, Level level, BlockPos pos, Player player) {} @Override - public void activate(Player player) {} + public void activate(Player player) { + //super is necessary here, because the override doesn't open the screen directly and instead opens the passcode screens + super.openCustomInventoryScreen(player); + } @Override public byte[] getPasscode() { diff --git a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java index 2cf02ab105..868b27745d 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java @@ -59,7 +59,7 @@ public OpenScreen(FriendlyByteBuf buf) { pos = buf.readBlockPos(); else if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) tag = buf.readNbt(); - else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY) + else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY) entityId = buf.readVarInt(); } @@ -71,7 +71,7 @@ public void write(FriendlyByteBuf buf) { buf.writeBlockPos(pos); else if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) buf.writeNbt(tag); - else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY) + else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY) buf.writeVarInt(entityId); } @@ -98,6 +98,11 @@ public void handle(PlayPayloadContext ctx) { if (!briefcaseStack.isEmpty()) ClientHandler.displayBriefcasePasscodeScreen(briefcaseStack.getHoverName()); + break; + case CHECK_PASSCODE_FOR_ENTITY: + if (Minecraft.getInstance().level.getEntity(entityId) instanceof IPasscodeProtected be) + ClientHandler.displayCheckPasscodeScreen((Entity) be); + break; case RIFT_STABILIZER: if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof RiftStabilizerBlockEntity riftStabilizer) @@ -149,6 +154,7 @@ public enum DataType { ALARM(true), CHECK_PASSCODE(true), CHECK_PASSCODE_FOR_BRIEFCASE(false), + CHECK_PASSCODE_FOR_ENTITY(false), RIFT_STABILIZER(true), SENTRY_REMOTE_ACCESS_TOOL(false), SET_PASSCODE(true), diff --git a/src/main/java/net/geforcemods/securitycraft/network/server/CheckPasscode.java b/src/main/java/net/geforcemods/securitycraft/network/server/CheckPasscode.java index 296f9d4a23..a57643a1fe 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/server/CheckPasscode.java +++ b/src/main/java/net/geforcemods/securitycraft/network/server/CheckPasscode.java @@ -10,28 +10,50 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import net.neoforged.neoforge.network.handling.PlayPayloadContext; public class CheckPasscode implements CustomPacketPayload { public static final ResourceLocation ID = new ResourceLocation(SecurityCraft.MODID, "check_passcode"); + private boolean isEntity; private BlockPos pos; + private int entityId; private String passcode; public CheckPasscode() {} public CheckPasscode(BlockPos pos, String passcode) { + this.isEntity = false; this.pos = pos; this.passcode = PasscodeUtils.hashPasscodeWithoutSalt(passcode); } + public CheckPasscode(int entityId, String passcode) { + this.isEntity = true; + this.entityId = entityId; + this.passcode = PasscodeUtils.hashPasscodeWithoutSalt(passcode); + } + public CheckPasscode(FriendlyByteBuf buf) { - pos = buf.readBlockPos(); + isEntity = buf.readBoolean(); + + if (isEntity) + entityId = buf.readVarInt(); + else + pos = buf.readBlockPos(); + passcode = buf.readUtf(Integer.MAX_VALUE / 4); } @Override public void write(FriendlyByteBuf buf) { - buf.writeBlockPos(pos); + buf.writeBoolean(isEntity); + + if (isEntity) + buf.writeVarInt(entityId); + else + buf.writeBlockPos(pos); + buf.writeUtf(passcode); } @@ -42,19 +64,31 @@ public ResourceLocation id() { public void handle(PlayPayloadContext ctx) { Player player = ctx.player().orElseThrow(); + IPasscodeProtected passcodeProtected = getPasscodeProtected(player.level()); - if (player.level().getBlockEntity(pos) instanceof IPasscodeProtected be) { - if (be.isOnCooldown()) + if (passcodeProtected != null) { + if (passcodeProtected.isOnCooldown()) return; - PasscodeUtils.hashPasscode(passcode, be.getSalt(), p -> { - if (Arrays.equals(be.getPasscode(), p)) { + PasscodeUtils.hashPasscode(passcode, passcodeProtected.getSalt(), p -> { + if (Arrays.equals(passcodeProtected.getPasscode(), p)) { player.closeContainer(); - be.activate(player); + passcodeProtected.activate(player); } else - be.onIncorrectPasscodeEntered(player, passcode); + passcodeProtected.onIncorrectPasscodeEntered(player, passcode); }); } } + + private IPasscodeProtected getPasscodeProtected(Level level) { + if (isEntity) { + if (level.getEntity(entityId) instanceof IPasscodeProtected pp) + return pp; + } + else if (level.getBlockEntity(pos) instanceof IPasscodeProtected pp) + return pp; + + return null; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/screen/CheckPasscodeScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/CheckPasscodeScreen.java index 973b643a14..d13bbaa51c 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/CheckPasscodeScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/CheckPasscodeScreen.java @@ -15,10 +15,10 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.PacketDistributor; @@ -26,7 +26,7 @@ public class CheckPasscodeScreen extends Screen { private static final ResourceLocation TEXTURE = new ResourceLocation("securitycraft:textures/gui/container/check_passcode.png"); private static final Component COOLDOWN_TEXT_1 = Component.translatable("gui.securitycraft:passcode.cooldown1"); private int cooldownText1XPos; - private IPasscodeProtected be; + private IPasscodeProtected passcodeProtected; private char[] allowedChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '\u0008', '\u001B' }; //0-9, backspace and escape @@ -37,9 +37,9 @@ public class CheckPasscodeScreen extends Screen { private CensoringEditBox keycodeTextbox; private boolean wasOnCooldownLastRenderTick = false; - public CheckPasscodeScreen(BlockEntity be, Component title) { + public CheckPasscodeScreen(IPasscodeProtected passcodeProtected, Component title) { super(title); - this.be = (IPasscodeProtected) be; + this.passcodeProtected = passcodeProtected; } @Override @@ -78,7 +78,7 @@ public boolean canConsumeInput() { keycodeTextbox.setMaxLength(Integer.MAX_VALUE); keycodeTextbox.setFilter(s -> s.matches("\\d*\\**")); //allow any amount of digits and any amount of asterisks - if (be.isOnCooldown()) + if (passcodeProtected.isOnCooldown()) toggleChildrenActive(false); else setInitialFocus(keycodeTextbox); @@ -89,8 +89,8 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia super.render(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.drawString(font, title, width / 2 - font.width(title) / 2, topPos + 6, 4210752, false); - if (be.isOnCooldown()) { - long cooldownEnd = be.getCooldownEnd(); + if (passcodeProtected.isOnCooldown()) { + long cooldownEnd = passcodeProtected.getCooldownEnd(); long secondsLeft = Math.max(cooldownEnd - System.currentTimeMillis(), 0) / 1000 + 1; //+1 so that the text doesn't say "0 seconds left" for a whole second Component text = Component.translatable("gui.securitycraft:passcode.cooldown2", secondsLeft); @@ -121,7 +121,7 @@ public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (minecraft.options.keyInventory.isActiveAndMatches(InputConstants.getKey(keyCode, scanCode))) onClose(); - if (!be.isOnCooldown() && (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER)) { + if (!passcodeProtected.isOnCooldown() && (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER)) { minecraft.player.playSound(SoundEvents.UI_BUTTON_CLICK.value(), 0.15F, 1.0F); checkCode(keycodeTextbox.getValue()); } @@ -137,7 +137,7 @@ public boolean isPauseScreen() { @Override public boolean charTyped(char typedChar, int keyCode) { - if (!be.isOnCooldown() && isValidChar(typedChar)) { + if (!passcodeProtected.isOnCooldown() && isValidChar(typedChar)) { keycodeTextbox.charTyped(typedChar, keyCode); minecraft.player.playSound(SoundEvents.UI_BUTTON_CLICK.value(), 0.15F, 1.0F); } @@ -172,13 +172,15 @@ private void toggleChildrenActive(boolean setActive) { } public void checkCode(String code) { - BlockPos pos = ((BlockEntity) be).getBlockPos(); - - if (be instanceof IModuleInventory moduleInv && moduleInv.isModuleEnabled(ModuleType.SMART)) + if (passcodeProtected instanceof IModuleInventory moduleInv && moduleInv.isModuleEnabled(ModuleType.SMART)) toggleChildrenActive(false); keycodeTextbox.setValue(""); - PacketDistributor.SERVER.noArg().send(new CheckPasscode(pos, code)); + + if (passcodeProtected instanceof BlockEntity be) + PacketDistributor.SERVER.noArg().send(new CheckPasscode(be.getBlockPos(), code)); + else if (passcodeProtected instanceof Entity entity) + PacketDistributor.SERVER.noArg().send(new CheckPasscode(entity.getId(), code)); } public static class CensoringEditBox extends EditBox { From 14918b0a72fa396b25b5deedcf8873f72ca0d992 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sat, 16 Mar 2024 16:16:03 +0100 Subject: [PATCH 07/55] add translations --- src/main/resources/assets/securitycraft/lang/de_at.json | 2 ++ src/main/resources/assets/securitycraft/lang/de_ch.json | 2 ++ src/main/resources/assets/securitycraft/lang/de_de.json | 2 ++ src/main/resources/assets/securitycraft/lang/en_us.json | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index c8b52b80e5..c6a103440b 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -896,6 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", + "help.securitycraft.security_sea_raft.info": "Das Sicherheits-Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -940,6 +941,7 @@ "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", + "item.securitycraft.security_sea_raft": "Sicherheits-Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index c8b52b80e5..c6a103440b 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -896,6 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", + "help.securitycraft.security_sea_raft.info": "Das Sicherheits-Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -940,6 +941,7 @@ "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", + "item.securitycraft.security_sea_raft": "Sicherheits-Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index c8b52b80e5..c6a103440b 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -896,6 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", + "help.securitycraft.security_sea_raft.info": "Das Sicherheits-Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -940,6 +941,7 @@ "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", + "item.securitycraft.security_sea_raft": "Sicherheits-Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index 8555ec2e7c..660b1ef60d 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -904,6 +904,7 @@ "help.securitycraft.scanner_trapdoor.info": "The Scanner Trapdoor acts like a Retinal Scanner combined with a Reinforced Trapdoor, where the owner looks at the trapdoor to open it.", "help.securitycraft.secret_signs.info": "The Secret Sign works like a vanilla sign, with the slight difference that only the owner of the sign and players on the allowlist can interact with, and view the text on it. Make sure no one can break the block it's placed on!", "help.securitycraft.security_camera.info": "The security camera allows you to view the nearby area from its position. Simply place down a camera, then rightclick it with a camera monitor to bind it for use.", + "help.securitycraft.security_sea_raft.info": "The Security Sea Raft is a chest boat with a Passcode-protected Chest. Anyone who has the correct code can open the chest. Only the owner can break the raft.", "help.securitycraft.sentry.info": "The Sentry attacks intruders with an infinite supply of bullets. Alternatively, other projectiles can be used by putting them in a passcode-protected chest or barrel below the Sentry. It has three modes and three target types that can be switched by rightclicking it. The modes are \"Idle\" (it will never attack), \"Camouflage\" (it will only activate once a target is within range) and \"Aggressive\" (it will always be active), with the latter two modes being available with the target types \"Hostile mobs only\", \"Hostile mobs and players\", and \"Players only\". A disguise module will make the Sentry disguise itself with the block inserted in the module, a speed module will increase the Sentry's attack speed, and an allowlist module will prohibit it from shooting listed players. The modules can be inserted or switched out by rightclicking a Sentry, and they can be extracted by using a Universal Block Modifier. To remove the Sentry, sneak-rightclick it.", "help.securitycraft.smart_module.info": "The smart module upgrades the range or functions of a block.", "help.securitycraft.sonic_security_system.info": "The Sonic Security System, inspired by the similarly-named device in the game \"Metroid Prime 2: Echoes\", is able to lock certain SecurityCraft blocks and prevent them from being used or interacted with. Right-clicking a supported block will bind it to the Sonic Security System. Once it is placed down, right-clicking it will open a GUI where you can enable, disable, or reset the Sonic Security System. The blocks can be unlocked by playing a tune nearby, either via the Portable Tune Player, or note blocks. To set such a tune, toggle note recording on in the Sonic Security System's GUI then play the tune that you want using note blocks played nearby. Toggle the recording off once you are finished. Any time that tune is played nearby the Sonic Security System, blocks locked by it will become accessible for a short while.", @@ -948,6 +949,7 @@ "item.securitycraft.remote_access_sentry": "Sentry Remote Access Tool", "item.securitycraft.sc_manual": "SecurityCraft Manual", "item.securitycraft.scanner_door_item": "Scanner Door", + "item.securitycraft.security_sea_raft": "Security Sea Raft", "item.securitycraft.sentry": "Sentry", "item.securitycraft.smart_module": "Smart Module", "item.securitycraft.speed_module": "Speed Module", From 777d51ac551265f7d9538b09b8ae0cd303d588c3 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 15:34:30 +0100 Subject: [PATCH 08/55] slightly adjust item texture --- .../textures/item/security_sea_raft.png | Bin 348 -> 354 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png b/src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png index a3569c21e89746d71eee4c6631676f660d7e0d08..851fb7fcf83168661009a1b6b6be670ef988ee60 100644 GIT binary patch delta 259 zcmcb^^oVJKqm+GsPl)Tng$u7;yY}qaGg~L;(o~aFL&hS%Mswl_OMTAL1z&LKA65m2C zb(MKxex9Lf*RD_6E_!ke!+a&y{3BB3uk2Wh1xyb_D5+YmT7GuEUt!+DdA_;pF6=eh sE_d@)+ePi~c5W-K-S9rw(be&TVZH>nmwE+z2+&;&p00i_>zopr0C3oI=l}o! delta 234 zcmVBs^78U^Wi*J0h>=?=2*v;e6#xJL6p@V= ze*jNU=Mew^010qNS#tmY4#NNd4#NS*Z>VGd004-ZL^Iw06}zCLKaU9uR+8 zdq}79DEE27-pKSi4gj9IH5iU0rr07*qoM6N<$f@Wk~p#T5? From 44890c974122a03fd6bca843267220027e8b8c36 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 15:43:53 +0100 Subject: [PATCH 09/55] add owner changer support --- .../securitycraft/entity/SecuritySeaRaft.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index a7d421910f..fdc42dca7c 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -10,6 +10,9 @@ import net.geforcemods.securitycraft.network.client.OpenScreen; import net.geforcemods.securitycraft.network.client.OpenScreen.DataType; import net.geforcemods.securitycraft.util.PasscodeUtils; +import net.geforcemods.securitycraft.util.PlayerUtils; +import net.geforcemods.securitycraft.util.Utils; +import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; @@ -23,6 +26,7 @@ import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.entity.vehicle.ChestBoat; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; @@ -55,9 +59,19 @@ public InteractionResult interactWithContainerVehicle(Player player) { Level level = level(); BlockPos pos = blockPosition(); - //TODO: Proper codebreaker support - if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player) && !player.isHolding(SCContent.CODEBREAKER.get())) - openPasscodeGUI(level, pos, player); + if (!level.isClientSide) { + ItemStack ownerChanger = PlayerUtils.getItemStackFromAnyHand(player, SCContent.UNIVERSAL_OWNER_CHANGER.get()); + + if (!ownerChanger.isEmpty() && isOwnedBy(player)) { + String newOwner = ownerChanger.getHoverName().getString(); + + setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); + } + //TODO: Proper codebreaker support + else if (verifyPasscodeSet(level, pos, this, player) && !player.isHolding(SCContent.CODEBREAKER.get())) + openPasscodeGUI(level, pos, player); + } return !level.isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } From e4601aa49ed31c820d0647a725f25ddecec827e2 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 15:44:25 +0100 Subject: [PATCH 10/55] allow creative players to destroy the raft --- .../net/geforcemods/securitycraft/entity/SecuritySeaRaft.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index fdc42dca7c..a38dd95544 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -96,7 +96,7 @@ public void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { public boolean hurt(DamageSource source, float amount) { Entity entity = source.getEntity(); - if (!(entity instanceof Player player) || isOwnedBy(player)) + if (!(entity instanceof Player player) || isOwnedBy(player) || player.isCreative()) return super.hurt(source, amount); else return false; From 268c3e8f6a72d0685b7897244278988ecc227206 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 15:49:34 +0100 Subject: [PATCH 11/55] remove salt key when the raft is destroyed --- .../geforcemods/securitycraft/entity/SecuritySeaRaft.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index a38dd95544..c363b0bd07 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -7,6 +7,7 @@ import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.api.Owner; import net.geforcemods.securitycraft.entity.sentry.Sentry; +import net.geforcemods.securitycraft.misc.SaltData; import net.geforcemods.securitycraft.network.client.OpenScreen; import net.geforcemods.securitycraft.network.client.OpenScreen.DataType; import net.geforcemods.securitycraft.util.PasscodeUtils; @@ -102,6 +103,12 @@ public boolean hurt(DamageSource source, float amount) { return false; } + @Override + public void chestVehicleDestroyed(DamageSource damageSource, Level level, Entity entity) { + super.chestVehicleDestroyed(damageSource, level, entity); + SaltData.removeSalt(getSaltKey()); + } + @Override protected void addAdditionalSaveData(CompoundTag tag) { CompoundTag ownerTag = new CompoundTag(); From 2e82d07ffc9e33a532fde458dc9b4e50035e142c Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 18:21:27 +0100 Subject: [PATCH 12/55] fix incorrect class being used for defining raft's owner data accessor --- .../net/geforcemods/securitycraft/entity/SecuritySeaRaft.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index c363b0bd07..66728f1034 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -6,7 +6,6 @@ import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.api.Owner; -import net.geforcemods.securitycraft.entity.sentry.Sentry; import net.geforcemods.securitycraft.misc.SaltData; import net.geforcemods.securitycraft.network.client.OpenScreen; import net.geforcemods.securitycraft.network.client.OpenScreen.DataType; @@ -33,7 +32,7 @@ import net.neoforged.neoforge.network.PacketDistributor; public class SecuritySeaRaft extends ChestBoat implements IOwnable, IPasscodeProtected { - private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(Sentry.class, Owner.getSerializer()); + private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaRaft.class, Owner.getSerializer()); private byte[] passcode; private UUID saltKey; From d39ea6ef29d6a7b4aaa3cd688ae9817be09aceb6 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 18:42:00 +0100 Subject: [PATCH 13/55] fix being able to change the owner while in the raft --- .../securitycraft/entity/SecuritySeaRaft.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 66728f1034..2cfea4aad2 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -18,6 +18,7 @@ import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; @@ -55,24 +56,35 @@ protected void defineSynchedData() { } @Override - public InteractionResult interactWithContainerVehicle(Player player) { - Level level = level(); - BlockPos pos = blockPosition(); + public InteractionResult interact(Player player, InteractionHand hand) { + if (player.isSecondaryUseActive()) { + ItemStack stack = player.getItemInHand(hand); - if (!level.isClientSide) { - ItemStack ownerChanger = PlayerUtils.getItemStackFromAnyHand(player, SCContent.UNIVERSAL_OWNER_CHANGER.get()); + if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) { + if (!player.level().isClientSide) { + String newOwner = stack.getHoverName().getString(); - if (!ownerChanger.isEmpty() && isOwnedBy(player)) { - String newOwner = ownerChanger.getHoverName().getString(); + setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); + return InteractionResult.CONSUME; + } - setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); + return InteractionResult.SUCCESS; } - //TODO: Proper codebreaker support - else if (verifyPasscodeSet(level, pos, this, player) && !player.isHolding(SCContent.CODEBREAKER.get())) - openPasscodeGUI(level, pos, player); } + return super.interact(player, hand); + } + + @Override + public InteractionResult interactWithContainerVehicle(Player player) { + Level level = level(); + BlockPos pos = blockPosition(); + + //TODO: Proper codebreaker support + if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player) && !player.isHolding(SCContent.CODEBREAKER.get())) + openPasscodeGUI(level, pos, player); + return !level.isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } From d51815ad7fc1edd66a24eff8922823ee163ffb38 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 18:45:55 +0100 Subject: [PATCH 14/55] adjust german translation --- src/main/resources/assets/securitycraft/lang/de_at.json | 4 ++-- src/main/resources/assets/securitycraft/lang/de_ch.json | 4 ++-- src/main/resources/assets/securitycraft/lang/de_de.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index c6a103440b..bb231601b9 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -896,7 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das Sicherheits-Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -941,7 +941,7 @@ "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", - "item.securitycraft.security_sea_raft": "Sicherheits-Seefloß", + "item.securitycraft.security_sea_raft": "Gesichertes Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index c6a103440b..bb231601b9 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -896,7 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das Sicherheits-Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -941,7 +941,7 @@ "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", - "item.securitycraft.security_sea_raft": "Sicherheits-Seefloß", + "item.securitycraft.security_sea_raft": "Gesichertes Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index c6a103440b..bb231601b9 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -896,7 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das Sicherheits-Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -941,7 +941,7 @@ "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", - "item.securitycraft.security_sea_raft": "Sicherheits-Seefloß", + "item.securitycraft.security_sea_raft": "Gesichertes Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", From 58070bc254b5abecd08513b680bf9b697720db48 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Sun, 17 Mar 2024 18:50:52 +0100 Subject: [PATCH 15/55] german clarification --- src/main/resources/assets/securitycraft/lang/de_at.json | 2 +- src/main/resources/assets/securitycraft/lang/de_ch.json | 2 +- src/main/resources/assets/securitycraft/lang/de_de.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index bb231601b9..913b839429 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -896,7 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index bb231601b9..913b839429 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -896,7 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index bb231601b9..913b839429 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -896,7 +896,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann sie öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen. Nur der Besitzer kann das Boot zerbrechen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", From a21ae496f5ec5894815fc42ee703e744bfa85ed9 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 25 Mar 2024 00:08:23 +0100 Subject: [PATCH 16/55] make raft codebreakable also small ICodebreakable refactoring --- .../securitycraft/SCEventHandler.java | 58 +-------------- .../securitycraft/api/ICodebreakable.java | 72 +++++++++++++++++-- .../securitycraft/api/IPasscodeProtected.java | 5 +- .../AbstractKeypadFurnaceBlockEntity.java | 4 +- .../blockentities/DisplayCaseBlockEntity.java | 4 +- .../blockentities/KeyPanelBlockEntity.java | 4 +- .../KeycardReaderBlockEntity.java | 6 +- .../blockentities/KeypadBlockEntity.java | 4 +- .../blockentities/KeypadDoorBlockEntity.java | 4 +- .../KeypadTrapdoorBlockEntity.java | 4 +- .../securitycraft/entity/SecuritySeaRaft.java | 11 ++- 11 files changed, 92 insertions(+), 84 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/SCEventHandler.java b/src/main/java/net/geforcemods/securitycraft/SCEventHandler.java index 2e1b3b6d4f..7f41906b78 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCEventHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/SCEventHandler.java @@ -23,7 +23,6 @@ import net.geforcemods.securitycraft.api.Owner; import net.geforcemods.securitycraft.api.SecurityCraftAPI; import net.geforcemods.securitycraft.blockentities.BlockChangeDetectorBlockEntity.DetectionMode; -import net.geforcemods.securitycraft.blockentities.DisplayCaseBlockEntity; import net.geforcemods.securitycraft.blockentities.ReinforcedLecternBlockEntity; import net.geforcemods.securitycraft.blockentities.RiftStabilizerBlockEntity; import net.geforcemods.securitycraft.blockentities.RiftStabilizerBlockEntity.TeleportationType; @@ -37,7 +36,6 @@ import net.geforcemods.securitycraft.entity.camera.CameraNightVisionEffectInstance; import net.geforcemods.securitycraft.entity.camera.SecurityCamera; import net.geforcemods.securitycraft.entity.sentry.Sentry; -import net.geforcemods.securitycraft.items.CodebreakerItem; import net.geforcemods.securitycraft.items.ModuleItem; import net.geforcemods.securitycraft.items.UniversalBlockReinforcerItem; import net.geforcemods.securitycraft.misc.BlockEntityTracker; @@ -53,7 +51,6 @@ import net.geforcemods.securitycraft.util.Utils; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; @@ -346,7 +343,8 @@ public static void onRightClickBlock(PlayerInteractEvent.RightClickBlock event) return; } - if (heldItem.is(SCContent.CODEBREAKER.get()) && handleCodebreaking(event)) { + if (heldItem.is(SCContent.CODEBREAKER.get()) && level.getBlockEntity(pos) instanceof ICodebreakable codebreakable) { + codebreakable.handleCodebreaking(player, event.getHand()); event.setCanceled(true); return; } @@ -560,56 +558,4 @@ private static void handlePlayedNote(Level level, BlockPos pos, int vanillaNoteI be.listenToNote(vanillaNoteId, instrument, customSoundId); } } - - private static boolean handleCodebreaking(PlayerInteractEvent.RightClickBlock event) { - Player player = event.getEntity(); - Level level = player.level(); - BlockPos pos = event.getPos(); - - if (level.getBlockEntity(pos) instanceof ICodebreakable codebreakable) { - if (codebreakable instanceof DisplayCaseBlockEntity displayCase && (displayCase.isOpen() && displayCase.getDisplayedStack().isEmpty())) - return false; - - double chance = ConfigHandler.SERVER.codebreakerChance.get(); - - if (chance < 0.0D) { - Block block = level.getBlockState(pos).getBlock(); - - PlayerUtils.sendMessageToPlayer(player, Utils.localize(block.getDescriptionId()), Utils.localize("messages.securitycraft:codebreakerDisabled"), ChatFormatting.RED); - } - else { - ItemStack codebreaker = player.getItemInHand(event.getHand()); - BlockState state = level.getBlockState(pos); - - if (!codebreakable.shouldAttemptCodebreak(state, player)) - return true; - - if (codebreaker.is(SCContent.CODEBREAKER.get())) { - if (codebreakable instanceof IOwnable ownable && ownable.isOwnedBy(player)) { - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.CODEBREAKER.get().getDescriptionId()), Utils.localize("messages.securitycraft:codebreaker.owned"), ChatFormatting.RED); - return false; - } - - if (CodebreakerItem.wasRecentlyUsed(codebreaker)) - return false; - - boolean isSuccessful = player.isCreative() || SecurityCraft.RANDOM.nextDouble() < chance; - CompoundTag tag = codebreaker.getOrCreateTag(); - - codebreaker.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(event.getHand())); - tag.putLong(CodebreakerItem.LAST_USED_TIME, System.currentTimeMillis()); - tag.putBoolean(CodebreakerItem.WAS_SUCCESSFUL, isSuccessful); - - if (isSuccessful) - codebreakable.useCodebreaker(state, player); - else - PlayerUtils.sendMessageToPlayer(player, Component.translatable(SCContent.CODEBREAKER.get().getDescriptionId()), Utils.localize("messages.securitycraft:codebreaker.failed"), ChatFormatting.RED); - } - } - - return true; - } - - return false; - } } diff --git a/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java b/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java index 45df25bfdd..753366e96f 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java +++ b/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java @@ -1,29 +1,87 @@ package net.geforcemods.securitycraft.api; +import net.geforcemods.securitycraft.ConfigHandler; +import net.geforcemods.securitycraft.SCContent; +import net.geforcemods.securitycraft.SecurityCraft; +import net.geforcemods.securitycraft.items.CodebreakerItem; +import net.geforcemods.securitycraft.util.PlayerUtils; +import net.geforcemods.securitycraft.util.Utils; +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.item.ItemStack; /** * Marks a block as being able to be hacked with the Codebreaker. * * @author Geforce */ +//TODO: Document removal of BlockState parameters in changelog +//TODO: Document new method in changelog public interface ICodebreakable { /** - * Checked before any codebreaking attempt, whether the codebreaker should attempt to break the code. Useful when the block + * Checked before any codebreaking attempt, whether the codebreaker should attempt to break the code. Useful when this * currently does not accept a code at all. * - * @param state The state of the block that the codebreaking attempt should be performed on * @param player The player trying the codebreaking attempt * @return true if the codebreaking attempt should be performed, false otherwise */ - public boolean shouldAttemptCodebreak(BlockState state, Player player); + public boolean shouldAttemptCodebreak(Player player); /** - * Called when a Codebreaker has successfully broken the code of a block. + * Called when a Codebreaker has successfully broken the code * - * @param state The block state of the block. * @param player The player who used the Codebreaker. */ - public void useCodebreaker(BlockState state, Player player); + public void useCodebreaker(Player player); + + /** + * Handles the actual breaking of the code alongside any player feedback. + * + * @param player The player trying the codebreaking attempt + * @param hand The hand holding the codebreaker + * @return true if the codebreaking attempt was successful, false otherwise + */ + public default boolean handleCodebreaking(Player player, InteractionHand hand) { + double chance = ConfigHandler.SERVER.codebreakerChance.get(); + + if (chance < 0.0D) + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.CODEBREAKER.get().getDescriptionId()), Utils.localize("messages.securitycraft:codebreakerDisabled"), ChatFormatting.RED); + else { + if (!shouldAttemptCodebreak(player)) + return false; + + ItemStack codebreaker = player.getItemInHand(hand); + + if (codebreaker.is(SCContent.CODEBREAKER.get())) { + if (this instanceof IOwnable ownable && ownable.isOwnedBy(player)) { + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.CODEBREAKER.get().getDescriptionId()), Utils.localize("messages.securitycraft:codebreaker.owned"), ChatFormatting.RED); + return false; + } + + if (CodebreakerItem.wasRecentlyUsed(codebreaker)) + return false; + + boolean isSuccessful = player.isCreative() || SecurityCraft.RANDOM.nextDouble() < chance; + CompoundTag tag = codebreaker.getOrCreateTag(); + + codebreaker.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(hand)); + tag.putLong(CodebreakerItem.LAST_USED_TIME, System.currentTimeMillis()); + tag.putBoolean(CodebreakerItem.WAS_SUCCESSFUL, isSuccessful); + + if (isSuccessful) + useCodebreaker(player); + else { + PlayerUtils.sendMessageToPlayer(player, Component.translatable(SCContent.CODEBREAKER.get().getDescriptionId()), Utils.localize("messages.securitycraft:codebreaker.failed"), ChatFormatting.RED); + return false; + } + } + else + return false; + } + + return true; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java index 2a6d3de842..fe659ad360 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java +++ b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java @@ -21,7 +21,6 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; // TODO: Remove mentions of "block entity" and generalize @@ -74,7 +73,7 @@ default void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { } @Override - default boolean shouldAttemptCodebreak(BlockState state, Player player) { + default boolean shouldAttemptCodebreak(Player player) { if (getPasscode() == null) { PlayerUtils.sendMessageToPlayer(player, Component.literal("SecurityCraft"), Utils.localize("messages.securitycraft:passcodeProtected.notSetUp"), ChatFormatting.DARK_RED); return false; @@ -84,7 +83,7 @@ default boolean shouldAttemptCodebreak(BlockState state, Player player) { } @Override - public default void useCodebreaker(BlockState state, Player player) { + public default void useCodebreaker(Player player) { activate(player); } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java index 14f07a921d..f9893667a4 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java @@ -191,13 +191,13 @@ public ItemStack getItem(int slot) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return IPasscodeProtected.super.shouldAttemptCodebreak(player); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java index c3858c87c4..e10fd24187 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java @@ -66,13 +66,13 @@ public void activate(Player player) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return !isOpen() && IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return !isOpen() && !getDisplayedStack().isEmpty() && IPasscodeProtected.super.shouldAttemptCodebreak(player); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java index 3fc8332ee9..bcd5f30b16 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java @@ -107,13 +107,13 @@ public void activate(Player player) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return !state.getValue(KeyPanelBlock.POWERED) && IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return !getBlockState().getValue(KeyPanelBlock.POWERED) && IPasscodeProtected.super.shouldAttemptCodebreak(player); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeycardReaderBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeycardReaderBlockEntity.java index d14071dfaf..48cf65511b 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeycardReaderBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeycardReaderBlockEntity.java @@ -103,17 +103,17 @@ public void load(CompoundTag tag) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return !state.getValue(BlockStateProperties.POWERED); + return !getBlockState().getValue(BlockStateProperties.POWERED); } @Override - public void useCodebreaker(BlockState state, Player player) { + public void useCodebreaker(Player player) { if (!level.isClientSide) activate(); } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBlockEntity.java index ebbde46b32..0bf8d72f7a 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBlockEntity.java @@ -74,13 +74,13 @@ public void activate(Player player) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return !state.getValue(KeypadBlock.POWERED) && IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return !getBlockState().getValue(KeypadBlock.POWERED) && IPasscodeProtected.super.shouldAttemptCodebreak(player); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadDoorBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadDoorBlockEntity.java index ea15ca1d93..a24c77646d 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadDoorBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadDoorBlockEntity.java @@ -73,13 +73,13 @@ public void activate(Player player) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return !state.getValue(DoorBlock.OPEN) && IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return !getBlockState().getValue(DoorBlock.OPEN) && IPasscodeProtected.super.shouldAttemptCodebreak(player); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadTrapdoorBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadTrapdoorBlockEntity.java index 61a2589aaf..fe37159b57 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadTrapdoorBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadTrapdoorBlockEntity.java @@ -73,13 +73,13 @@ public void activate(Player player) { } @Override - public boolean shouldAttemptCodebreak(BlockState state, Player player) { + public boolean shouldAttemptCodebreak(Player player) { if (isDisabled()) { player.displayClientMessage(Utils.localize("gui.securitycraft:scManual.disabled"), true); return false; } - return !state.getValue(TrapDoorBlock.OPEN) && IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return !getBlockState().getValue(TrapDoorBlock.OPEN) && IPasscodeProtected.super.shouldAttemptCodebreak(player); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 2cfea4aad2..43245a972d 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -32,6 +32,8 @@ import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; +// TODO: Change "This block doesn't have a passcode yet" message to not mention block +// TODO: Change all other passcode-related lines to stop talking about blocks public class SecuritySeaRaft extends ChestBoat implements IOwnable, IPasscodeProtected { private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaRaft.class, Owner.getSerializer()); private byte[] passcode; @@ -81,9 +83,12 @@ public InteractionResult interactWithContainerVehicle(Player player) { Level level = level(); BlockPos pos = blockPosition(); - //TODO: Proper codebreaker support - if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player) && !player.isHolding(SCContent.CODEBREAKER.get())) - openPasscodeGUI(level, pos, player); + if (!level.isClientSide) { + if (player.isHolding(SCContent.CODEBREAKER.get())) + handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + else if (verifyPasscodeSet(level, pos, this, player)) + openPasscodeGUI(level, pos, player); + } return !level.isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } From 2fb2e94e136266b38f10d7f2c71f563d914df3e0 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 25 Mar 2024 11:42:04 +0100 Subject: [PATCH 17/55] make key changer work on raft --- .../securitycraft/ClientHandler.java | 6 ++++- .../securitycraft/entity/SecuritySeaRaft.java | 7 ++++++ .../items/UniversalKeyChangerItem.java | 2 +- .../network/client/OpenScreen.java | 24 ++++++++++++------- .../screen/KeyChangerScreen.java | 14 +++++++---- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index cbea41bd22..99e7df8418 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -716,7 +716,11 @@ public static void displayUsernameLoggerScreen(BlockPos pos) { } public static void displayUniversalKeyChangerScreen(BlockEntity be) { - Minecraft.getInstance().setScreen(new KeyChangerScreen(be)); + Minecraft.getInstance().setScreen(new KeyChangerScreen((IPasscodeProtected) be)); + } + + public static void displayUniversalKeyChangerScreen(Entity entity) { + Minecraft.getInstance().setScreen(new KeyChangerScreen((IPasscodeProtected) entity)); } public static void displayCheckPasscodeScreen(BlockEntity be) { diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 43245a972d..1aaf4deee3 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -34,6 +34,7 @@ // TODO: Change "This block doesn't have a passcode yet" message to not mention block // TODO: Change all other passcode-related lines to stop talking about blocks +// TODO: Remove mention of "block" in messages.securitycraft:notOwned public class SecuritySeaRaft extends ChestBoat implements IOwnable, IPasscodeProtected { private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaRaft.class, Owner.getSerializer()); private byte[] passcode; @@ -86,6 +87,12 @@ public InteractionResult interactWithContainerVehicle(Player player) { if (!level.isClientSide) { if (player.isHolding(SCContent.CODEBREAKER.get())) handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) { + if (isOwnedBy(player) || player.isCreative()) + PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE_FOR_ENTITY, getId())); + else + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); + } else if (verifyPasscodeSet(level, pos, this, player)) openPasscodeGUI(level, pos, player); } diff --git a/src/main/java/net/geforcemods/securitycraft/items/UniversalKeyChangerItem.java b/src/main/java/net/geforcemods/securitycraft/items/UniversalKeyChangerItem.java index e8a85c9637..1ea74b4129 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/UniversalKeyChangerItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/UniversalKeyChangerItem.java @@ -50,7 +50,7 @@ public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext ctx) { else if (be instanceof IPasscodeProtected) { if (((IOwnable) be).isOwnedBy(player) || player.isCreative()) { if (!level.isClientSide) - PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.UNIVERSAL_KEY_CHANGER, pos)); + PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE, pos)); return InteractionResult.SUCCESS; } diff --git a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java index 868b27745d..a8b7f34fac 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/network/client/OpenScreen.java @@ -59,7 +59,7 @@ public OpenScreen(FriendlyByteBuf buf) { pos = buf.readBlockPos(); else if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) tag = buf.readNbt(); - else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY) + else if (dataType == DataType.CHANGE_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY || dataType == DataType.SET_PASSCODE_FOR_ENTITY) entityId = buf.readVarInt(); } @@ -71,7 +71,7 @@ public void write(FriendlyByteBuf buf) { buf.writeBlockPos(pos); else if (dataType == DataType.SENTRY_REMOTE_ACCESS_TOOL) buf.writeNbt(tag); - else if (dataType == DataType.SET_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY) + else if (dataType == DataType.CHANGE_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY || dataType == DataType.SET_PASSCODE_FOR_ENTITY) buf.writeVarInt(entityId); } @@ -86,6 +86,16 @@ public void handle(PlayPayloadContext ctx) { if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof AlarmBlockEntity be) ClientHandler.displayAlarmScreen(be); + break; + case CHANGE_PASSCODE: + if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) + ClientHandler.displayUniversalKeyChangerScreen((BlockEntity) be); + + break; + case CHANGE_PASSCODE_FOR_ENTITY: + if (Minecraft.getInstance().level.getEntity(entityId) instanceof IPasscodeProtected be) + ClientHandler.displayUniversalKeyChangerScreen((Entity) be); + break; case CHECK_PASSCODE: if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) @@ -139,11 +149,6 @@ public void handle(PlayPayloadContext ctx) { if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof SonicSecuritySystemBlockEntity sss) ClientHandler.displaySonicSecuritySystemScreen(sss); - break; - case UNIVERSAL_KEY_CHANGER: - if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected passcodeProtected) - ClientHandler.displayUniversalKeyChangerScreen((BlockEntity) passcodeProtected); - break; default: throw new IllegalStateException("Unhandled data type: " + dataType.name()); @@ -152,6 +157,8 @@ public void handle(PlayPayloadContext ctx) { public enum DataType { ALARM(true), + CHANGE_PASSCODE(true), + CHANGE_PASSCODE_FOR_ENTITY(false), CHECK_PASSCODE(true), CHECK_PASSCODE_FOR_BRIEFCASE(false), CHECK_PASSCODE_FOR_ENTITY(false), @@ -160,8 +167,7 @@ public enum DataType { SET_PASSCODE(true), SET_PASSCODE_FOR_BRIEFCASE(false), SET_PASSCODE_FOR_ENTITY(false), - SONIC_SECURITY_SYSTEM(true), - UNIVERSAL_KEY_CHANGER(true); + SONIC_SECURITY_SYSTEM(true); public final boolean needsPosition; diff --git a/src/main/java/net/geforcemods/securitycraft/screen/KeyChangerScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/KeyChangerScreen.java index a32873850f..87b2573be3 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/KeyChangerScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/KeyChangerScreen.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.platform.InputConstants; import net.geforcemods.securitycraft.SCContent; +import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.network.server.SetPasscode; import net.geforcemods.securitycraft.util.PlayerUtils; import net.geforcemods.securitycraft.util.Utils; @@ -14,6 +15,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.PacketDistributor; @@ -29,11 +31,11 @@ public class KeyChangerScreen extends Screen { private EditBox textboxNewPasscode; private EditBox textboxConfirmPasscode; private Button confirmButton; - private BlockEntity be; + private IPasscodeProtected passcodeProtected; - public KeyChangerScreen(BlockEntity be) { + public KeyChangerScreen(IPasscodeProtected passcodeProtected) { super(Component.translatable(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId())); - this.be = be; + this.passcodeProtected = passcodeProtected; } @Override @@ -95,7 +97,11 @@ private void updateConfirmButtonState() { } private void confirmButtonClicked(Button button) { - PacketDistributor.SERVER.noArg().send(new SetPasscode(be.getBlockPos(), textboxNewPasscode.getValue())); + if (passcodeProtected instanceof BlockEntity be) + PacketDistributor.SERVER.noArg().send(new SetPasscode(be.getBlockPos(), textboxNewPasscode.getValue())); + else if (passcodeProtected instanceof Entity entity) + PacketDistributor.SERVER.noArg().send(new SetPasscode(entity.getId(), textboxNewPasscode.getValue())); + Minecraft.getInstance().player.closeContainer(); PlayerUtils.sendMessageToPlayer(Minecraft.getInstance().player, Utils.localize(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalKeyChanger.passcodeChanged"), ChatFormatting.GREEN, true); } From ed32871162e95bff38cffe2ebaff96f03f0debff Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Tue, 26 Mar 2024 11:06:49 +0100 Subject: [PATCH 18/55] make codebreaker and key changer no longer work when sitting in the boat --- .../securitycraft/entity/SecuritySeaRaft.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 1aaf4deee3..53310b74ef 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -63,7 +63,15 @@ public InteractionResult interact(Player player, InteractionHand hand) { if (player.isSecondaryUseActive()) { ItemStack stack = player.getItemInHand(hand); - if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) { + if (player.isHolding(SCContent.CODEBREAKER.get())) + handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) { + if (isOwnedBy(player) || player.isCreative()) + PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE_FOR_ENTITY, getId())); + else + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); + } + else if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) { if (!player.level().isClientSide) { String newOwner = stack.getHoverName().getString(); @@ -84,18 +92,8 @@ public InteractionResult interactWithContainerVehicle(Player player) { Level level = level(); BlockPos pos = blockPosition(); - if (!level.isClientSide) { - if (player.isHolding(SCContent.CODEBREAKER.get())) - handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); - else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) { - if (isOwnedBy(player) || player.isCreative()) - PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE_FOR_ENTITY, getId())); - else - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); - } - else if (verifyPasscodeSet(level, pos, this, player)) - openPasscodeGUI(level, pos, player); - } + if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player)) + openPasscodeGUI(level, pos, player); return !level.isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } From 77144fc3e0439f089015eb88bd30556ea65c98d3 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Tue, 26 Mar 2024 16:04:35 +0100 Subject: [PATCH 19/55] fix crash --- .../securitycraft/entity/SecuritySeaRaft.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 53310b74ef..53dfe73778 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -62,25 +62,33 @@ protected void defineSynchedData() { public InteractionResult interact(Player player, InteractionHand hand) { if (player.isSecondaryUseActive()) { ItemStack stack = player.getItemInHand(hand); + Level level = player.level(); - if (player.isHolding(SCContent.CODEBREAKER.get())) - handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + if (player.isHolding(SCContent.CODEBREAKER.get())) { + if (!level.isClientSide) + handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + + return InteractionResult.sidedSuccess(level.isClientSide); + } else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) { - if (isOwnedBy(player) || player.isCreative()) - PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE_FOR_ENTITY, getId())); - else - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); + if (!level.isClientSide) { + if (isOwnedBy(player) || player.isCreative()) + PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE_FOR_ENTITY, getId())); + else + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_KEY_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); + } + + return InteractionResult.sidedSuccess(level.isClientSide); } else if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) { - if (!player.level().isClientSide) { + if (!level.isClientSide) { String newOwner = stack.getHoverName().getString(); setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); - return InteractionResult.CONSUME; } - return InteractionResult.SUCCESS; + return InteractionResult.sidedSuccess(level.isClientSide); } } From 92519fd84fafc0344f63ad371bb6fddf17af4963 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Wed, 15 May 2024 13:29:19 +0200 Subject: [PATCH 20/55] refactor IModuleInventory to allow more than just block entities --- Changelog.md | 1 + .../api/CustomizableBlockEntity.java | 11 +++++ .../securitycraft/api/IModuleInventory.java | 47 +++++++------------ .../geforcemods/securitycraft/api/Option.java | 27 +++++------ .../AbstractKeypadFurnaceBlockEntity.java | 10 ++++ .../AllowlistOnlyBlockEntity.java | 8 ++-- .../ElectrifiedFenceAndGateBlockEntity.java | 2 +- .../GlowDisplayCaseBlockEntity.java | 4 +- .../KeypadBarrelBlockEntity.java | 10 ++++ .../blockentities/KeypadChestBlockEntity.java | 10 ++++ ...einforcedChiseledBookshelfBlockEntity.java | 11 +++++ .../ReinforcedDispenserBlockEntity.java | 11 +++++ .../ReinforcedFenceGateBlockEntity.java | 2 +- .../ReinforcedHopperBlockEntity.java | 11 +++++ .../ReinforcedLecternBlockEntity.java | 11 +++++ .../RetinalScannerBlockEntity.java | 3 +- .../blockentities/ScannerDoorBlockEntity.java | 3 +- .../ScannerTrapdoorBlockEntity.java | 3 +- .../SecretHangingSignBlockEntity.java | 18 +++++-- .../blockentities/SecretSignBlockEntity.java | 18 +++++-- .../inventory/CustomizeBlockMenu.java | 20 ++++++-- .../screen/CustomizeBlockScreen.java | 30 ++++++------ .../securitycraft/screen/SCManualScreen.java | 5 +- .../securitycraft/util/BlockUtils.java | 10 ++++ 24 files changed, 200 insertions(+), 86 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2169326e7f..09570ea9b3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ - Change: Players in creative mode can once again use the codebreaker on their own blocks - API: Changed constructors for IntOption and DoubleOption, they are now always sliders by default - API: Removed FloatOption. Use DoubleOption instead +- API: IModuleInventory is no longer hardcoded to just block entities - Fix: Trying to place a Panic Button on top of powdered snow crashes the game - Fix: Occasional crash when opening the inventory in creative mode in certain situations - Fix: Reinforced fence gates don't properly retain their owner when reloading the world diff --git a/src/main/java/net/geforcemods/securitycraft/api/CustomizableBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/api/CustomizableBlockEntity.java index 2cb0d5811e..806055d0d7 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/CustomizableBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/api/CustomizableBlockEntity.java @@ -8,6 +8,7 @@ import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -59,4 +60,14 @@ public boolean isModuleEnabled(ModuleType module) { public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { moduleStates.put(module, shouldBeEnabled); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/api/IModuleInventory.java b/src/main/java/net/geforcemods/securitycraft/api/IModuleInventory.java index e5288655a9..8fe808f2a7 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/IModuleInventory.java +++ b/src/main/java/net/geforcemods/securitycraft/api/IModuleInventory.java @@ -56,12 +56,9 @@ public interface IModuleInventory extends IItemHandlerModifiable { */ public void toggleModuleState(ModuleType module, boolean shouldBeEnabled); - /** - * @return The block entity this inventory is for - */ - public default BlockEntity getBlockEntity() { - return (BlockEntity) this; - } + public Level myLevel(); + + public BlockPos myPos(); /** * @return The amount of modules that can be inserted @@ -78,11 +75,9 @@ public default int getMaxNumberOfModules() { * @param toggled false if the actual item changed, true if the enabled state of the module changed */ public default void onModuleInserted(ItemStack stack, ModuleType module, boolean toggled) { - BlockEntity be = getBlockEntity(); - toggleModuleState(module, true); - if (!be.getLevel().isClientSide) { + if (this instanceof BlockEntity be && !be.getLevel().isClientSide) { be.setChanged(); be.getLevel().sendBlockUpdated(be.getBlockPos(), be.getBlockState(), be.getBlockState(), 3); } @@ -96,11 +91,9 @@ public default void onModuleInserted(ItemStack stack, ModuleType module, boolean * @param toggled false if the actual item changed, true if the enabled state of the module changed */ public default void onModuleRemoved(ItemStack stack, ModuleType module, boolean toggled) { - BlockEntity be = getBlockEntity(); - toggleModuleState(module, false); - if (!be.getLevel().isClientSide) { + if (this instanceof BlockEntity be && !be.getLevel().isClientSide) { be.setChanged(); be.getLevel().sendBlockUpdated(be.getBlockPos(), be.getBlockState(), be.getBlockState(), 3); } @@ -128,18 +121,14 @@ public default int fixSlotId(int id) { } public default void dropAllModules() { - BlockEntity be = getBlockEntity(); - Level level = be.getLevel(); - BlockPos pos = be.getBlockPos(); - for (ItemStack module : getInventory()) { if (!(module.getItem() instanceof ModuleItem)) continue; - if (be instanceof LinkableBlockEntity linkable) - linkable.propagate(new ILinkedAction.ModuleRemoved(((ModuleItem) module.getItem()).getModuleType(), false), linkable); + if (this instanceof LinkableBlockEntity be) + be.propagate(new ILinkedAction.ModuleRemoved(((ModuleItem) module.getItem()).getModuleType(), false), be); - Block.popResource(level, pos, module); + Block.popResource(myLevel(), myPos(), module); } getInventory().clear(); @@ -175,7 +164,7 @@ public default ItemStack extractItem(int slot, int amount, boolean simulate) { if (stack.getItem() instanceof ModuleItem module) { onModuleRemoved(stack, module.getModuleType(), false); - if (getBlockEntity() instanceof LinkableBlockEntity be) + if (this instanceof LinkableBlockEntity be) be.propagate(new ILinkedAction.ModuleRemoved(((ModuleItem) stack.getItem()).getModuleType(), false), be); } @@ -208,7 +197,7 @@ public default ItemStack insertItem(int slot, ItemStack stack, boolean simulate) if (stack.getItem() instanceof ModuleItem module) { onModuleInserted(stack, module.getModuleType(), false); - if (getBlockEntity() instanceof LinkableBlockEntity be) + if (this instanceof LinkableBlockEntity be) be.propagate(new ILinkedAction.ModuleInserted(copy, (ModuleItem) copy.getItem(), false), be); } } @@ -238,7 +227,7 @@ public default void setStackInSlot(int slot, ItemStack stack) { if (!previous.isEmpty()) { onModuleRemoved(previous, ((ModuleItem) previous.getItem()).getModuleType(), false); - if (getBlockEntity() instanceof LinkableBlockEntity be) + if (this instanceof LinkableBlockEntity be) be.propagate(new ILinkedAction.ModuleRemoved(((ModuleItem) previous.getItem()).getModuleType(), false), be); } @@ -247,7 +236,7 @@ public default void setStackInSlot(int slot, ItemStack stack) { if (stack.getItem() instanceof ModuleItem module) { onModuleInserted(stack, module.getModuleType(), false); - if (getBlockEntity() instanceof LinkableBlockEntity be) + if (this instanceof LinkableBlockEntity be) be.propagate(new ILinkedAction.ModuleInserted(stack, (ModuleItem) stack.getItem(), false), be); } } @@ -499,7 +488,7 @@ public default boolean isAllowed(String name) { return true; //IModuleInventory#getModule returns ItemStack.EMPTY when the module does not exist, and getPlayersFromModule will then have an empty list - return ModuleItem.doesModuleHaveTeamOf(stack, name, getBlockEntity().getLevel()) || ModuleItem.getPlayersFromModule(stack).contains(name.toLowerCase()); + return ModuleItem.doesModuleHaveTeamOf(stack, name, myLevel()) || ModuleItem.getPlayersFromModule(stack).contains(name.toLowerCase()); } /** @@ -515,7 +504,7 @@ public default boolean isDenied(Entity entity) { ItemStack stack = getModule(ModuleType.DENYLIST); if (stack.hasTag() && stack.getTag().getBoolean("affectEveryone")) { - if (getBlockEntity() instanceof IOwnable ownable) { + if (this instanceof IOwnable ownable) { //only deny players that are not the owner if (entity instanceof Player player) { //if the player IS the owner, fall back to the default handling (check if the name is on the list) @@ -532,7 +521,7 @@ public default boolean isDenied(Entity entity) { String name = entity.getName().getString(); //IModuleInventory#getModule returns ItemStack.EMPTY when the module does not exist, and getPlayersFromModule will then have an empty list - return ModuleItem.doesModuleHaveTeamOf(stack, name, getBlockEntity().getLevel()) || ModuleItem.getPlayersFromModule(stack).contains(name.toLowerCase()); + return ModuleItem.doesModuleHaveTeamOf(stack, name, myLevel()) || ModuleItem.getPlayersFromModule(stack).contains(name.toLowerCase()); } /** @@ -548,11 +537,11 @@ public default boolean shouldDropModules() { * Get the description text's translation key that is shown in the customize screen tooltip when hovering over a module * button * - * @param blockName The name of the block that is being customized + * @param denotation The denotation to use for the key, usually the block's name * @param module The type of the module whose module button is being hovered * @return The translation key to use for the description */ - public default String getModuleDescriptionId(String blockName, ModuleType module) { - return "module." + blockName + "." + module.getTranslationKey().substring(5).replace("securitycraft.", "") + ".description"; + public default String getModuleDescriptionId(String denotation, ModuleType module) { + return "module." + denotation + "." + module.getTranslationKey().substring(5).replace("securitycraft.", "") + ".description"; } } \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/api/Option.java b/src/main/java/net/geforcemods/securitycraft/api/Option.java index 770a2c06ac..5edd1fb821 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/Option.java +++ b/src/main/java/net/geforcemods/securitycraft/api/Option.java @@ -5,7 +5,6 @@ import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.world.level.block.Block; /** * A class that allows blocks that have {@link ICustomizable} block entities to have custom, per-block options that are @@ -110,19 +109,19 @@ public boolean isSlider() { } /** - * @param block The block this option is a part of + * @param denotation The denotation to use for the option key, usually the block's name * @return The language key for this option */ - public String getKey(Block block) { - return "option." + block.getDescriptionId().substring(6) + "." + getName(); + public String getKey(String denotation) { + return "option." + denotation + "." + getName(); } /** - * @param block The block this option is a part of + * @param denotation The denotation to use for the option key, usually the block's name * @return The language key for the description of this option */ - public String getDescriptionKey(Block block) { - return getKey(block) + ".description"; + public String getDescriptionKey(String denotation) { + return getKey(denotation) + ".description"; } /** @@ -175,7 +174,7 @@ public DisabledOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.disabled"; } } @@ -186,7 +185,7 @@ public IgnoreOwnerOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.ignoreOwner"; } } @@ -197,7 +196,7 @@ public SendAllowlistMessageOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.sendAllowlistMessage"; } } @@ -208,7 +207,7 @@ public SendDenylistMessageOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.sendDenylistMessage"; } } @@ -249,7 +248,7 @@ public SmartModuleCooldownOption() { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.smartModuleCooldown"; } } @@ -260,7 +259,7 @@ public SignalLengthOption(int defaultLength) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.signalLength"; } } @@ -348,7 +347,7 @@ public TargetingModeOption(TargetingMode defaultValue) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.targetingMode"; } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java index f9893667a4..918e38c24f 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java @@ -339,4 +339,14 @@ public boolean sendsDenylistMessage() { public boolean isDisabled() { return disabled.get(); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/AllowlistOnlyBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/AllowlistOnlyBlockEntity.java index 41afdabe3e..f101609e16 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/AllowlistOnlyBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/AllowlistOnlyBlockEntity.java @@ -30,12 +30,12 @@ public Option[] customOptions() { } @Override - public String getModuleDescriptionId(String blockName, ModuleType module) { - if (blockName.contains("pressure")) + public String getModuleDescriptionId(String denotation, ModuleType module) { + if (denotation.contains("pressure")) return super.getModuleDescriptionId("generic.reinforced_pressure_plate", module); - else if (blockName.contains("button")) + else if (denotation.contains("button")) return super.getModuleDescriptionId("generic.reinforced_button", module); else - return super.getModuleDescriptionId(blockName, module); + return super.getModuleDescriptionId(denotation, module); } } \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ElectrifiedFenceAndGateBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ElectrifiedFenceAndGateBlockEntity.java index 14e7c8eb39..465c9f2046 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ElectrifiedFenceAndGateBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ElectrifiedFenceAndGateBlockEntity.java @@ -16,7 +16,7 @@ public ElectrifiedFenceAndGateBlockEntity(BlockEntityType type, BlockPos pos, } @Override - public String getModuleDescriptionId(String blockName, ModuleType module) { + public String getModuleDescriptionId(String denotation, ModuleType module) { return "module.generic.electrified_fence_and_gate.whitelist_module.description"; } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/GlowDisplayCaseBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/GlowDisplayCaseBlockEntity.java index 027b30fd21..652ded8a72 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/GlowDisplayCaseBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/GlowDisplayCaseBlockEntity.java @@ -11,7 +11,7 @@ public GlowDisplayCaseBlockEntity(BlockPos pos, BlockState state) { } @Override - public String getModuleDescriptionId(String blockName, ModuleType module) { - return super.getModuleDescriptionId(blockName.replace("glow_", ""), module); + public String getModuleDescriptionId(String denotation, ModuleType module) { + return super.getModuleDescriptionId(denotation.replace("glow_", ""), module); } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBarrelBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBarrelBlockEntity.java index 7d3f401232..2595ed0cf7 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBarrelBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadBarrelBlockEntity.java @@ -391,4 +391,14 @@ public void setPreviousBarrel(Block previousBarrel) { public ResourceLocation getPreviousBarrel() { return previousBarrel; } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java index 6a99737ed0..5a3522c943 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java @@ -406,4 +406,14 @@ public void setPreviousChest(Block previousChest) { public ResourceLocation getPreviousChest() { return previousChest; } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedChiseledBookshelfBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedChiseledBookshelfBlockEntity.java index 8d87ab3006..371edc4e74 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedChiseledBookshelfBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedChiseledBookshelfBlockEntity.java @@ -14,6 +14,7 @@ import net.minecraft.network.Connection; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -105,4 +106,14 @@ public boolean isModuleEnabled(ModuleType module) { public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { moduleStates.put(module, shouldBeEnabled); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedDispenserBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedDispenserBlockEntity.java index ede89f3d54..abda7a3a18 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedDispenserBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedDispenserBlockEntity.java @@ -18,6 +18,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.DispenserBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -134,4 +135,14 @@ public boolean isModuleEnabled(ModuleType module) { public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { moduleStates.put(module, shouldBeEnabled); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedFenceGateBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedFenceGateBlockEntity.java index fe0f3f56df..5f77814955 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedFenceGateBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedFenceGateBlockEntity.java @@ -11,7 +11,7 @@ public ReinforcedFenceGateBlockEntity(BlockPos pos, BlockState state) { } @Override - public String getModuleDescriptionId(String blockName, ModuleType module) { + public String getModuleDescriptionId(String denotation, ModuleType module) { return super.getModuleDescriptionId("generic.reinforced_fence_gate", module); } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java index 0131522cf8..8ba21d50ef 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java @@ -18,6 +18,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.HopperBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -140,4 +141,14 @@ public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { public boolean needsValidation() { return true; } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedLecternBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedLecternBlockEntity.java index a34b2c6542..be4f4ea187 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedLecternBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedLecternBlockEntity.java @@ -22,6 +22,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.LecternBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -136,4 +137,14 @@ public AbstractContainerMenu createMenu(int containerId, Inventory playerInvento public Component getDisplayName() { return Component.translatable(SCContent.REINFORCED_LECTERN.get().getDescriptionId()); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/RetinalScannerBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/RetinalScannerBlockEntity.java index 87a3b5a8f3..0f6f735979 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/RetinalScannerBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/RetinalScannerBlockEntity.java @@ -37,7 +37,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; @@ -50,7 +49,7 @@ public class RetinalScannerBlockEntity extends DisguisableBlockEntity implements private IntOption signalLength = new SignalLengthOption(60); private DoubleOption maximumDistance = new DoubleOption("maximumDistance", 5.0D, 0.1D, 25.0D, 0.1D) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.viewActivated.maximumDistance"; } }; diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerDoorBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerDoorBlockEntity.java index de63b801b2..c0b283386e 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerDoorBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerDoorBlockEntity.java @@ -21,7 +21,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.DoorBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; @@ -32,7 +31,7 @@ public class ScannerDoorBlockEntity extends SpecialDoorBlockEntity implements IV private BooleanOption sendMessage = new BooleanOption("sendMessage", true); private DoubleOption maximumDistance = new DoubleOption("maximumDistance", 5.0D, 0.1D, 25.0D, 0.1D) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.viewActivated.maximumDistance"; } }; diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerTrapdoorBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerTrapdoorBlockEntity.java index a2eeaf5c23..14e0156131 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerTrapdoorBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ScannerTrapdoorBlockEntity.java @@ -25,7 +25,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.HorizontalDirectionalBlock; import net.minecraft.world.level.block.TrapDoorBlock; import net.minecraft.world.level.block.state.BlockState; @@ -36,7 +35,7 @@ public class ScannerTrapdoorBlockEntity extends CustomizableBlockEntity implemen protected IntOption signalLength = new IntOption("signalLength", 0, 0, 400, 5); //20 seconds max private DoubleOption maximumDistance = new DoubleOption("maximumDistance", 5.0D, 0.1D, 25.0D, 0.1D) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.viewActivated.maximumDistance"; } }; diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/SecretHangingSignBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/SecretHangingSignBlockEntity.java index 5da24a8c0a..10f503e1c4 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/SecretHangingSignBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/SecretHangingSignBlockEntity.java @@ -18,7 +18,7 @@ import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.HangingSignBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -27,13 +27,13 @@ public class SecretHangingSignBlockEntity extends HangingSignBlockEntity impleme private Owner owner = new Owner(); private BooleanOption isFrontSecret = new BooleanOption("isFrontSecret", true) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.secret_sign.isFrontSecret"; } }; private BooleanOption isBackSecret = new BooleanOption("isBackSecret", true) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.secret_sign.isBackSecret"; } }; @@ -161,7 +161,17 @@ public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { } @Override - public String getModuleDescriptionId(String blockName, ModuleType module) { + public String getModuleDescriptionId(String denotation, ModuleType module) { return IModuleInventory.super.getModuleDescriptionId("generic.secret_sign", module); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/SecretSignBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/SecretSignBlockEntity.java index 24b9958abe..8e25e7137e 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/SecretSignBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/SecretSignBlockEntity.java @@ -18,7 +18,7 @@ import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -27,13 +27,13 @@ public class SecretSignBlockEntity extends SignBlockEntity implements IOwnable, private Owner owner = new Owner(); private BooleanOption isFrontSecret = new BooleanOption("isFrontSecret", true) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.secret_sign.isFrontSecret"; } }; private BooleanOption isBackSecret = new BooleanOption("isBackSecret", true) { @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.secret_sign.isBackSecret"; } }; @@ -161,7 +161,17 @@ public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { } @Override - public String getModuleDescriptionId(String blockName, ModuleType module) { + public String getModuleDescriptionId(String denotation, ModuleType module) { return IModuleInventory.super.getModuleDescriptionId("generic.secret_sign", module); } + + @Override + public Level myLevel() { + return level; + } + + @Override + public BlockPos myPos() { + return worldPosition; + } } diff --git a/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java b/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java index 6fc5f35604..796f9b2db7 100644 --- a/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java +++ b/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java @@ -19,13 +19,23 @@ public class CustomizeBlockMenu extends AbstractContainerMenu { public final IModuleInventory moduleInv; private ContainerLevelAccess worldPosCallable; - public final int maxSlots; + private int maxSlots; public CustomizeBlockMenu(int windowId, Level level, BlockPos pos, Inventory inventory) { super(SCContent.CUSTOMIZE_BLOCK_MENU.get(), windowId); - this.moduleInv = (IModuleInventory) level.getBlockEntity(pos); + moduleInv = (IModuleInventory) level.getBlockEntity(pos); worldPosCallable = ContainerLevelAccess.create(level, pos); + addSlots(inventory); + } + public CustomizeBlockMenu(int windowId, Level level, BlockPos pos, int entityId, Inventory inventory) { + super(SCContent.CUSTOMIZE_BLOCK_MENU.get(), windowId); + moduleInv = (IModuleInventory) level.getEntity(entityId); + worldPosCallable = ContainerLevelAccess.create(level, pos); + addSlots(inventory); + } + + public void addSlots(Inventory inventory) { int slotId = 0; for (int i = 0; i < 3; i++) { @@ -110,7 +120,11 @@ else if (!moveItemStackTo(slotStack, 27, 36, false)) //hotbar @Override public boolean stillValid(Player player) { - return stillValid(worldPosCallable, player, moduleInv.getBlockEntity().getBlockState().getBlock()); + return worldPosCallable.evaluate((level, pos) -> player.distanceToSqr(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5) <= 64.0, true); //TODO: 1.20.6: reach attribute + } + + public int getMaxSlots() { + return maxSlots; } private class CustomSlotItemHandler extends SlotItemHandler { diff --git a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java index 6e3d2da771..d83cb6a962 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java @@ -19,6 +19,7 @@ import net.geforcemods.securitycraft.network.server.UpdateSliderValue; import net.geforcemods.securitycraft.screen.components.CallbackSlider; import net.geforcemods.securitycraft.screen.components.PictureButton; +import net.geforcemods.securitycraft.util.BlockUtils; import net.geforcemods.securitycraft.util.IHasExtraAreas; import net.geforcemods.securitycraft.util.Utils; import net.minecraft.ChatFormatting; @@ -35,7 +36,6 @@ import net.minecraft.world.inventory.ContainerListener; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; import net.neoforged.neoforge.network.PacketDistributor; public class CustomizeBlockScreen extends AbstractContainerScreen implements IHasExtraAreas, ContainerListener { @@ -55,14 +55,12 @@ public class CustomizeBlockScreen extends AbstractContainerScreen indicators = new EnumMap<>(ModuleType.class); public CustomizeBlockScreen(CustomizeBlockMenu menu, Inventory inv, Component title) { super(menu, inv, title); moduleInv = menu.moduleInv; - block = menu.moduleInv.getBlockEntity().getBlockState().getBlock(); maxNumberOfModules = moduleInv.getMaxNumberOfModules(); menu.addSlotListener(this); @@ -88,7 +86,7 @@ public void init() { descriptionButtons[i].active = moduleInv.hasModule(moduleInv.acceptedModules()[i]); } - if (moduleInv.getBlockEntity() instanceof ICustomizable customizable) { + if (moduleInv instanceof ICustomizable customizable) { Option[] options = customizable.customOptions(); if (options.length > 0) { @@ -101,19 +99,19 @@ public void init() { if (option instanceof DoubleOption doubleOption) { final int sliderIndex = i; - optionButtons[i] = new CallbackSlider(leftPos + 178, (topPos + 10) + (i * 25), 120, 20, Utils.localize(option.getKey(block), ""), Component.empty(), doubleOption.getMin(), doubleOption.getMax(), doubleOption.get(), doubleOption.getIncrement(), 0, true, slider -> { + optionButtons[i] = new CallbackSlider(leftPos + 178, (topPos + 10) + (i * 25), 120, 20, Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), ""), Component.empty(), doubleOption.getMin(), doubleOption.getMax(), doubleOption.get(), doubleOption.getIncrement(), 0, true, slider -> { doubleOption.setValue(slider.getValue()); optionButtons[sliderIndex].setTooltip(Tooltip.create(getOptionDescription(sliderIndex))); - PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.getBlockEntity().getBlockPos(), option, doubleOption.get())); + PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.myPos(), option, doubleOption.get())); }); } else if (option instanceof IntOption intOption) { final int sliderIndex = i; - optionButtons[i] = new CallbackSlider(leftPos + 178, (topPos + 10) + (i * 25), 120, 20, Utils.localize(option.getKey(block), ""), Component.empty(), intOption.getMin(), intOption.getMax(), intOption.get(), true, slider -> { + optionButtons[i] = new CallbackSlider(leftPos + 178, (topPos + 10) + (i * 25), 120, 20, Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), ""), Component.empty(), intOption.getMin(), intOption.getMax(), intOption.get(), true, slider -> { intOption.setValue(slider.getValueInt()); optionButtons[sliderIndex].setTooltip(Tooltip.create(getOptionDescription(sliderIndex))); - PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.getBlockEntity().getBlockPos(), option, intOption.get())); + PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.myPos(), option, intOption.get())); }); } @@ -139,7 +137,7 @@ else if (option instanceof IntOption intOption) { public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { super.render(guiGraphics, mouseX, mouseY, partialTicks); - for (int i = 36; i < menu.maxSlots; i++) { + for (int i = 36; i < menu.getMaxSlots(); i++) { Slot slot = menu.slots.get(i); if (!slot.getItem().isEmpty()) { @@ -201,7 +199,7 @@ private void moduleButtonClicked(Button button) { moduleInv.insertModule(moduleInv.getModule(moduleType), true); } - PacketDistributor.SERVER.noArg().send(new ToggleModule(moduleInv.getBlockEntity().getBlockPos(), moduleType)); + PacketDistributor.SERVER.noArg().send(new ToggleModule(moduleInv.myPos(), moduleType)); } private void optionButtonClicked(Button button) { @@ -209,13 +207,13 @@ private void optionButtonClicked(Button button) { if (button != optionButtons[i]) continue; - Option tempOption = ((ICustomizable) moduleInv.getBlockEntity()).customOptions()[i]; //safe cast, as this method is only called when it can be casted + Option tempOption = ((ICustomizable) moduleInv).customOptions()[i]; //safe cast, as this method is only called when it can be casted tempOption.toggle(); button.setFGColor(tempOption.toString().equals(tempOption.getDefaultValue().toString()) ? 16777120 : 14737632); button.setMessage(getOptionButtonTitle(tempOption)); optionButtons[i].setTooltip(Tooltip.create(getOptionDescription(i))); - PacketDistributor.SERVER.noArg().send(new ToggleOption(moduleInv.getBlockEntity().getBlockPos(), i)); + PacketDistributor.SERVER.noArg().send(new ToggleOption(moduleInv.myPos(), i)); return; } } @@ -226,18 +224,18 @@ private Component getModuleTooltipText(int moduleId) { .append(Component.literal(":")) .withStyle(ChatFormatting.RESET) .append(Component.literal("\n\n")) - .append(Utils.localize(moduleInv.getModuleDescriptionId(block.getDescriptionId().substring(6), ((ModuleItem) descriptionButtons[moduleId].getItemStack().getItem()).getModuleType()))); + .append(Utils.localize(moduleInv.getModuleDescriptionId(BlockUtils.getLanguageKeyDenotation(moduleInv), ((ModuleItem) descriptionButtons[moduleId].getItemStack().getItem()).getModuleType()))); //@formatter:on } private Component getOptionDescription(int optionId) { - Option option = ((ICustomizable) moduleInv.getBlockEntity()).customOptions()[optionId]; + Option option = ((ICustomizable) moduleInv).customOptions()[optionId]; - return Utils.localize("gui.securitycraft:customize.tooltip", Component.translatable(option.getDescriptionKey(block)), Component.translatable("gui.securitycraft:customize.currentSetting", getValueText(option))); + return Utils.localize("gui.securitycraft:customize.tooltip", Component.translatable(option.getDescriptionKey(BlockUtils.getLanguageKeyDenotation(moduleInv))), Component.translatable("gui.securitycraft:customize.currentSetting", getValueText(option))); } private Component getOptionButtonTitle(Option option) { - return Utils.localize(option.getKey(block), getValueText(option)); + return Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), getValueText(option)); } private Component getValueText(Option option) { diff --git a/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java index 12ba3214ca..f7c6e8eb24 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java @@ -34,6 +34,7 @@ import net.geforcemods.securitycraft.screen.components.HoverChecker; import net.geforcemods.securitycraft.screen.components.IngredientDisplay; import net.geforcemods.securitycraft.screen.components.TextHoverChecker; +import net.geforcemods.securitycraft.util.BlockUtils; import net.geforcemods.securitycraft.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; @@ -503,7 +504,7 @@ else if (recipe != null) { display.add(Component.literal("---")); for (Option option : options) { - display.add(Component.translatable("gui.securitycraft:scManual.option_text", Component.translatable(option.getDescriptionKey(block)), option.getDefaultInfo())); + display.add(Component.translatable("gui.securitycraft:scManual.option_text", Component.translatable(option.getDescriptionKey(BlockUtils.getLanguageKeyDenotation(customizableBe))), option.getDefaultInfo())); display.add(Component.empty()); } @@ -520,7 +521,7 @@ else if (recipe != null) { display.add(Component.literal("---")); for (ModuleType module : moduleInv.acceptedModules()) { - display.add(Component.literal("- ").append(Utils.localize(moduleInv.getModuleDescriptionId(block.getDescriptionId().substring(6), module)))); + display.add(Component.literal("- ").append(Utils.localize(moduleInv.getModuleDescriptionId(BlockUtils.getLanguageKeyDenotation(moduleInv), module)))); display.add(Component.empty()); } diff --git a/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java b/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java index 6ffebee49c..3c8296272a 100644 --- a/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java +++ b/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java @@ -10,6 +10,7 @@ import net.geforcemods.securitycraft.api.SecurityCraftAPI; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.Level.ExplosionInteraction; @@ -125,4 +126,13 @@ public static void removeInSequence(BiPredicate stateMatc } } } + + public static String getLanguageKeyDenotation(Object obj) { + if (obj instanceof BlockEntity be) + return be.getBlockState().getBlock().getDescriptionId().substring(6); + else if (obj instanceof Entity entity) + return entity.getType().toShortString(); + else + return ""; + } } From cbf69bcb7f47e1a244cf164f417e7349959a201c Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Wed, 15 May 2024 16:04:55 +0200 Subject: [PATCH 21/55] make raft not placeable on lava --- .../securitycraft/items/SecuritySeaRaftItem.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java index 43597a0586..5ac27bf6da 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java @@ -2,13 +2,18 @@ import net.geforcemods.securitycraft.entity.SecuritySeaRaft; import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.item.BoatItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; @@ -17,6 +22,16 @@ public SecuritySeaRaftItem(Item.Properties properties) { super(true, Boat.Type.BAMBOO, properties); } + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + BlockHitResult hitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.ANY); + + if (!level.getFluidState(hitResult.getBlockPos()).is(FluidTags.LAVA)) + return super.use(level, player, hand); + else + return InteractionResultHolder.fail(player.getItemInHand(hand)); + } + @Override public Boat getBoat(Level level, HitResult hitResult, ItemStack stack, Player player) { Vec3 vec3 = hitResult.getLocation(); From 3f8bc79f526c05b8cab034535a9c859d6c3b3594 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 10:08:54 +0200 Subject: [PATCH 22/55] make raft invulnerable to all damage type apart from player damage --- Changelog.md | 1 + .../damage_type/security_sea_boat_vulnerable_to.json | 5 +++++ .../net/geforcemods/securitycraft/SCContent.java | 1 + .../java/net/geforcemods/securitycraft/SCTags.java | 12 ++++++++++++ .../datagen/DamageTypeTagGenerator.java | 4 ++++ .../securitycraft/entity/SecuritySeaRaft.java | 6 ++++++ 6 files changed, 29 insertions(+) create mode 100644 src/generated/resources/data/securitycraft/tags/damage_type/security_sea_boat_vulnerable_to.json diff --git a/Changelog.md b/Changelog.md index 09570ea9b3..77b57c1c94 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ - New: Server config setting "allow_camera_night_vision" to set whether players are able to activate night vision without having the actual potion effect - New: Pressing "Enter" while typing a player name in an Allowlist/Denylist Module will now add the player to the list without needing to press the "Add Player" button +- New: Damage Type Tag "securitycraft:security_sea_boat_vulnerable_to" to define which damage types the Security Sea Boat can be destroyed by - Change: The cameraSpeed client side config setting has been moved to be a per-block option, accessible with the Universal Block Modifier - Change: Some SecurityCraft tip messages have been reworded for clarity - Change: Increased suffocation damage inside reinforced blocks no longer affects non-player entities and players owning the reinforced blocks diff --git a/src/generated/resources/data/securitycraft/tags/damage_type/security_sea_boat_vulnerable_to.json b/src/generated/resources/data/securitycraft/tags/damage_type/security_sea_boat_vulnerable_to.json new file mode 100644 index 0000000000..5531338df7 --- /dev/null +++ b/src/generated/resources/data/securitycraft/tags/damage_type/security_sea_boat_vulnerable_to.json @@ -0,0 +1,5 @@ +{ + "values": [ + "minecraft:player_attack" + ] +} \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index 24cbca3d27..a63a8499a9 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -2926,6 +2926,7 @@ else if (state.is(BOUNCING_BETTY)) () -> EntityType.Builder.of(SecuritySeaRaft::new, MobCategory.MISC) .sized(1.375F, 0.5625F) .clientTrackingRange(10) + .fireImmune() .build(SecurityCraft.MODID + ":security_sea_raft")); //@formatter:on diff --git a/src/main/java/net/geforcemods/securitycraft/SCTags.java b/src/main/java/net/geforcemods/securitycraft/SCTags.java index 1207ec0182..61bed8c27e 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCTags.java +++ b/src/main/java/net/geforcemods/securitycraft/SCTags.java @@ -1,9 +1,11 @@ package net.geforcemods.securitycraft; +import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BlockTags; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; +import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; @@ -65,6 +67,16 @@ private static TagKey tag(String name) { } } + public static class DamageTypes { + private DamageTypes() {} + + public static final TagKey SECURITY_SEA_BOAT_VULNERABLE_TO = tag("security_sea_boat_vulnerable_to"); + + private static TagKey tag(String name) { + return TagKey.create(Registries.DAMAGE_TYPE, new ResourceLocation(SecurityCraft.MODID, name)); + } + } + public static class Items { private Items() {} diff --git a/src/main/java/net/geforcemods/securitycraft/datagen/DamageTypeTagGenerator.java b/src/main/java/net/geforcemods/securitycraft/datagen/DamageTypeTagGenerator.java index 6d0c355ae6..818d08c846 100644 --- a/src/main/java/net/geforcemods/securitycraft/datagen/DamageTypeTagGenerator.java +++ b/src/main/java/net/geforcemods/securitycraft/datagen/DamageTypeTagGenerator.java @@ -2,6 +2,7 @@ import java.util.concurrent.CompletableFuture; +import net.geforcemods.securitycraft.SCTags; import net.geforcemods.securitycraft.SecurityCraft; import net.geforcemods.securitycraft.misc.CustomDamageSources; import net.minecraft.core.HolderLookup.Provider; @@ -10,6 +11,7 @@ import net.minecraft.data.tags.TagsProvider; import net.minecraft.tags.DamageTypeTags; import net.minecraft.world.damagesource.DamageType; +import net.minecraft.world.damagesource.DamageTypes; import net.neoforged.neoforge.common.data.ExistingFileHelper; public class DamageTypeTagGenerator extends TagsProvider { @@ -19,6 +21,8 @@ protected DamageTypeTagGenerator(PackOutput output, CompletableFuture @Override protected void addTags(Provider provider) { + tag(SCTags.DamageTypes.SECURITY_SEA_BOAT_VULNERABLE_TO).add(DamageTypes.PLAYER_ATTACK); + tag(DamageTypeTags.BYPASSES_ARMOR).add(CustomDamageSources.FAKE_WATER, CustomDamageSources.ELECTRICITY, CustomDamageSources.IN_REINFORCED_WALL); tag(DamageTypeTags.BYPASSES_EFFECTS).add(CustomDamageSources.IN_REINFORCED_WALL); } diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 53dfe73778..884f676430 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -3,6 +3,7 @@ import java.util.UUID; import net.geforcemods.securitycraft.SCContent; +import net.geforcemods.securitycraft.SCTags; import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.api.Owner; @@ -132,6 +133,11 @@ public boolean hurt(DamageSource source, float amount) { return false; } + @Override + public boolean isInvulnerableTo(DamageSource source) { + return !source.is(SCTags.DamageTypes.SECURITY_SEA_BOAT_VULNERABLE_TO) || super.isInvulnerableTo(source); + } + @Override public void chestVehicleDestroyed(DamageSource damageSource, Level level, Entity entity) { super.chestVehicleDestroyed(damageSource, level, entity); From d604e6c0d957619fc4c9ac9d8d35da11b6aeab37 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 10:17:29 +0200 Subject: [PATCH 23/55] add recipe --- .../recipes/security_sea_raft.json | 15 ++++++++++----- .../datagen/RecipeGenerator.java | 19 ++++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json b/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json index 68f65171c6..9da0f0556c 100644 --- a/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json +++ b/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json @@ -1,13 +1,18 @@ { - "type": "minecraft:crafting_shapeless", + "type": "minecraft:crafting_shaped", "category": "misc", - "ingredients": [ - { + "group": "securitycraft:security_sea_boats", + "key": { + "C": { "item": "securitycraft:keypad_chest" }, - { - "item": "minecraft:bamboo_raft" + "P": { + "item": "securitycraft:reinforced_bamboo_planks" } + }, + "pattern": [ + "PCP", + "PPP" ], "result": { "item": "securitycraft:security_sea_raft" diff --git a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java index 1426989e0d..db7392420c 100644 --- a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java +++ b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java @@ -668,11 +668,6 @@ protected final void buildRecipes(RecipeOutput recipeOutput) { .requires(SCContent.RETINAL_SCANNER.get()) .unlockedBy("has_reinforced_trapdoor", has(SCContent.REINFORCED_IRON_TRAPDOOR.get())) .save(recipeOutput); - ShapelessRecipeBuilder.shapeless(RecipeCategory.TRANSPORTATION, SCContent.SECURITY_SEA_RAFT_ITEM.get()) - .requires(SCContent.KEYPAD_CHEST.get()) - .requires(Items.BAMBOO_RAFT) - .unlockedBy("has_keypad_chest", has(SCContent.KEYPAD_CHEST.get())) - .save(recipeOutput); ShapelessRecipeBuilder.shapeless(RecipeCategory.TOOLS, SCContent.UNIVERSAL_OWNER_CHANGER.get()) .requires(SCContent.UNIVERSAL_BLOCK_MODIFIER.get()) .requires(Items.NAME_TAG) @@ -886,6 +881,7 @@ protected final void buildRecipes(RecipeOutput recipeOutput) { addSecretSignRecipe(recipeOutput, Items.OAK_HANGING_SIGN, SCContent.SECRET_OAK_HANGING_SIGN.get()); addSecretSignRecipe(recipeOutput, Items.SPRUCE_HANGING_SIGN, SCContent.SECRET_SPRUCE_HANGING_SIGN.get()); addSecretSignRecipe(recipeOutput, Items.WARPED_HANGING_SIGN, SCContent.SECRET_WARPED_HANGING_SIGN.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_BAMBOO_PLANKS.get(), SCContent.SECURITY_SEA_RAFT_ITEM.get()); addSlabRecipe(recipeOutput, Ingredient.of(SCContent.CRYSTAL_QUARTZ_BLOCK.get(), SCContent.CRYSTAL_QUARTZ_PILLAR.get(), SCContent.CHISELED_CRYSTAL_QUARTZ.get()), SCContent.CRYSTAL_QUARTZ_SLAB.get()); addSlabRecipe(recipeOutput, SCContent.REINFORCED_ACACIA_PLANKS.get(), SCContent.REINFORCED_ACACIA_SLAB.get()); addSlabRecipe(recipeOutput, SCContent.REINFORCED_ANDESITE.get(), SCContent.REINFORCED_ANDESITE_SLAB.get()); @@ -1569,6 +1565,19 @@ protected final void addSecretSignRecipe(RecipeOutput recipeOutput, ItemLike van //@formatter:on } + protected final void addSecuritySeaBoatRecipe(RecipeOutput recipeOutput, ItemLike planks, ItemLike boat) { + //@formatter:off + ShapedRecipeBuilder.shaped(RecipeCategory.TRANSPORTATION, boat) + .group("securitycraft:security_sea_boats") + .pattern("PCP") + .pattern("PPP") + .define('P', planks) + .define('C', SCContent.KEYPAD_CHEST.get()) + .unlockedBy("has_keypad_chest", has(SCContent.KEYPAD_CHEST.get())) + .save(recipeOutput); + //@formatter:on + } + protected final void addSimpleCookingRecipe(RecipeOutput recipeOutput, ItemLike input, ItemLike output) { addSimpleCookingRecipe(recipeOutput, input, output, 0.1F, 200); } From af9679f0e5792eb01c2fb8b7e6af21d28abe825c Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 10:23:29 +0200 Subject: [PATCH 24/55] adjust some messages to remove mention of "block" --- .../net/geforcemods/securitycraft/entity/SecuritySeaRaft.java | 3 --- src/main/resources/assets/securitycraft/lang/de_at.json | 4 ++-- src/main/resources/assets/securitycraft/lang/de_ch.json | 4 ++-- src/main/resources/assets/securitycraft/lang/de_de.json | 4 ++-- src/main/resources/assets/securitycraft/lang/en_us.json | 4 ++-- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java index 884f676430..35a826324e 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java @@ -33,9 +33,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; -// TODO: Change "This block doesn't have a passcode yet" message to not mention block -// TODO: Change all other passcode-related lines to stop talking about blocks -// TODO: Remove mention of "block" in messages.securitycraft:notOwned public class SecuritySeaRaft extends ChestBoat implements IOwnable, IPasscodeProtected { private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaRaft.class, Owner.getSerializer()); private byte[] passcode; diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index 0ac3bae097..4e4adc3207 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -1035,10 +1035,10 @@ "messages.securitycraft:mrat.unbound": "Mine bei %s von der Einheit getrennt.", "messages.securitycraft:naming.alreadyMatches": "Der Name dieses Blocks ist schon '%s'.", "messages.securitycraft:naming.named": "Dieser Block wurde zu '%s' umbenannt.", - "messages.securitycraft:notOwned": "Entschuldigung, du kannst das nicht mit diesem Block tun. Er gehört %s.", + "messages.securitycraft:notOwned": "Entschuldigung, du kannst das nicht tun. Dies gehört %s.", "messages.securitycraft:ownable.ownerNotValidated": "Der Besitzer dieses Blocks wurde von einem Universellen Besitzeränderer geändert und muss deshalb vom neuen Besitzer verifiziert werden, um wieder zu funktionieren. Um einen Block zu verifizieren, muss der neue Besitzer des Blocks den Block rechtsklicken.", "messages.securitycraft:ownable.validate": "Der Besitzer dieses Blocks wurde erfolgreich verifiziert.", - "messages.securitycraft:passcodeProtected.notSetUp": "Dieser Block hat noch kein Passwort. Du kannst nicht auf ihn zugreifen.", + "messages.securitycraft:passcodeProtected.notSetUp": "Es existiert noch kein Passwort. Du kannst nicht hierauf zugreifen.", "messages.securitycraft:portableRadar.withName": "%1$s ist in der Nähe deines tragbaren Radars namens %2$s bei %3$2.", "messages.securitycraft:portableRadar.withoutName": "%1$s ist in der Nähe deines tragbaren Radars bei %2$s.", "messages.securitycraft:portable_tune_player.no_tune": "Keine Melodie vorhanden.", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index 0ac3bae097..4e4adc3207 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -1035,10 +1035,10 @@ "messages.securitycraft:mrat.unbound": "Mine bei %s von der Einheit getrennt.", "messages.securitycraft:naming.alreadyMatches": "Der Name dieses Blocks ist schon '%s'.", "messages.securitycraft:naming.named": "Dieser Block wurde zu '%s' umbenannt.", - "messages.securitycraft:notOwned": "Entschuldigung, du kannst das nicht mit diesem Block tun. Er gehört %s.", + "messages.securitycraft:notOwned": "Entschuldigung, du kannst das nicht tun. Dies gehört %s.", "messages.securitycraft:ownable.ownerNotValidated": "Der Besitzer dieses Blocks wurde von einem Universellen Besitzeränderer geändert und muss deshalb vom neuen Besitzer verifiziert werden, um wieder zu funktionieren. Um einen Block zu verifizieren, muss der neue Besitzer des Blocks den Block rechtsklicken.", "messages.securitycraft:ownable.validate": "Der Besitzer dieses Blocks wurde erfolgreich verifiziert.", - "messages.securitycraft:passcodeProtected.notSetUp": "Dieser Block hat noch kein Passwort. Du kannst nicht auf ihn zugreifen.", + "messages.securitycraft:passcodeProtected.notSetUp": "Es existiert noch kein Passwort. Du kannst nicht hierauf zugreifen.", "messages.securitycraft:portableRadar.withName": "%1$s ist in der Nähe deines tragbaren Radars namens %2$s bei %3$2.", "messages.securitycraft:portableRadar.withoutName": "%1$s ist in der Nähe deines tragbaren Radars bei %2$s.", "messages.securitycraft:portable_tune_player.no_tune": "Keine Melodie vorhanden.", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index 0ac3bae097..4e4adc3207 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -1035,10 +1035,10 @@ "messages.securitycraft:mrat.unbound": "Mine bei %s von der Einheit getrennt.", "messages.securitycraft:naming.alreadyMatches": "Der Name dieses Blocks ist schon '%s'.", "messages.securitycraft:naming.named": "Dieser Block wurde zu '%s' umbenannt.", - "messages.securitycraft:notOwned": "Entschuldigung, du kannst das nicht mit diesem Block tun. Er gehört %s.", + "messages.securitycraft:notOwned": "Entschuldigung, du kannst das nicht tun. Dies gehört %s.", "messages.securitycraft:ownable.ownerNotValidated": "Der Besitzer dieses Blocks wurde von einem Universellen Besitzeränderer geändert und muss deshalb vom neuen Besitzer verifiziert werden, um wieder zu funktionieren. Um einen Block zu verifizieren, muss der neue Besitzer des Blocks den Block rechtsklicken.", "messages.securitycraft:ownable.validate": "Der Besitzer dieses Blocks wurde erfolgreich verifiziert.", - "messages.securitycraft:passcodeProtected.notSetUp": "Dieser Block hat noch kein Passwort. Du kannst nicht auf ihn zugreifen.", + "messages.securitycraft:passcodeProtected.notSetUp": "Es existiert noch kein Passwort. Du kannst nicht hierauf zugreifen.", "messages.securitycraft:portableRadar.withName": "%1$s ist in der Nähe deines tragbaren Radars namens %2$s bei %3$2.", "messages.securitycraft:portableRadar.withoutName": "%1$s ist in der Nähe deines tragbaren Radars bei %2$s.", "messages.securitycraft:portable_tune_player.no_tune": "Keine Melodie vorhanden.", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index b087848194..d65567b0e5 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -1044,10 +1044,10 @@ "messages.securitycraft:mrat.unbound": "Unbound the mine at %s from the remote access tool.", "messages.securitycraft:naming.alreadyMatches": "This block's name already matches '%s'.", "messages.securitycraft:naming.named": "This block has been renamed to '%s'.", - "messages.securitycraft:notOwned": "Sorry, you cannot do that to this block. It is owned by %s.", + "messages.securitycraft:notOwned": "Sorry, you cannot do that. This is owned by %s.", "messages.securitycraft:ownable.ownerNotValidated": "This block's owner has been changed by the Universal Owner Changer and thus needs to be validated by the new owner to work again. To validate a block, the new owner of the block has to rightclick it.", "messages.securitycraft:ownable.validate": "The owner of this block has been successfully validated.", - "messages.securitycraft:passcodeProtected.notSetUp": "This block doesn't have a passcode yet. You cannot interact with it.", + "messages.securitycraft:passcodeProtected.notSetUp": "There is no passcode set yet. You cannot interact with this.", "messages.securitycraft:portableRadar.withName": "%1$s is near your portable radar named %2$s at %3$s.", "messages.securitycraft:portableRadar.withoutName": "%1$s is near your portable radar at %2$s.", "messages.securitycraft:portable_tune_player.no_tune": "No tune available.", From 87a0b2eac303413e8ef35fcae3719411324c8d53 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 10:26:29 +0200 Subject: [PATCH 25/55] update changelog with changes to ICodebreakable --- Changelog.md | 2 ++ .../net/geforcemods/securitycraft/api/ICodebreakable.java | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 77b57c1c94..a22cc1b5f3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,8 @@ - API: Changed constructors for IntOption and DoubleOption, they are now always sliders by default - API: Removed FloatOption. Use DoubleOption instead - API: IModuleInventory is no longer hardcoded to just block entities +- API: New method ICodebreakable#handleCodebreaking to define behavior when a codebreaker is used to break the code +- API: The BlockState parameters in ICodebreakable's methods have been removed - Fix: Trying to place a Panic Button on top of powdered snow crashes the game - Fix: Occasional crash when opening the inventory in creative mode in certain situations - Fix: Reinforced fence gates don't properly retain their owner when reloading the world diff --git a/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java b/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java index 46c64f8832..68c56b8d14 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java +++ b/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java @@ -14,12 +14,10 @@ import net.minecraft.world.item.ItemStack; /** - * Marks a block as being able to be hacked with the Codebreaker. + * Marks an object as being able to be hacked with the Codebreaker. * * @author Geforce */ -//TODO: Document removal of BlockState parameters in changelog -//TODO: Document new method in changelog public interface ICodebreakable { /** * Checked before any codebreaking attempt, whether the codebreaker should attempt to break the code. Useful when this From 1c5ff1d5f7f35f62cf25e802950c1eae260cf95d Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 10:29:50 +0200 Subject: [PATCH 26/55] add and adjust some javadoc --- .../securitycraft/api/IPasscodeProtected.java | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java index fe659ad360..8c605b985f 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java +++ b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java @@ -23,11 +23,10 @@ import net.minecraft.world.level.Level; import net.neoforged.neoforge.network.PacketDistributor; -// TODO: Remove mentions of "block entity" and generalize /** - * Implementing this interface designates a block entity as being passcode-protected. Implementing this allows you to use - * {@link SetPasscodeScreen} and {@link CheckPasscodeScreen} to easily set your block's passcode. Extends - * {@link ICodebreakable} as most passcode-protected blocks are likely able to be hacked using the Codebreaker by default. + * Implementing this interface designates an object as being passcode-protected. Implementing this allows you to use + * {@link SetPasscodeScreen} and {@link CheckPasscodeScreen} to easily set your object's passcode. Extends + * {@link ICodebreakable} as most passcode-protected object are likely able to be hacked using the Codebreaker by default. * * @author Geforce */ @@ -35,8 +34,8 @@ public interface IPasscodeProtected extends ICodebreakable { /** * Open the check passcode GUI if a passcode is set.

* - * @param level The level of this block entity - * @param pos The position of this block entity + * @param level The level of this object + * @param pos The position of this object * @param player The player who the GUI should be opened to. */ public default void openPasscodeGUI(Level level, BlockPos pos, Player player) { @@ -47,10 +46,10 @@ public default void openPasscodeGUI(Level level, BlockPos pos, Player player) { /** * Check that a passcode has been set, and if not, opens the set passcode screen or sends a warning message.

* - * @param level The level of this block entity - * @param pos The position of this block entity - * @param ownable This block entity - * @param player The player who interacted with this block entity + * @param level The level of this object + * @param pos The position of this object + * @param ownable This object + * @param player The player who interacted with this object * @return true if a passcode has been set, false otherwise */ default boolean verifyPasscodeSet(Level level, BlockPos pos, IOwnable ownable, Player player) { @@ -67,7 +66,12 @@ default boolean verifyPasscodeSet(Level level, BlockPos pos, IOwnable ownable, P return false; } - //TODO: Javadoc + /** + * Opens the screen to set the object's passcode + * + * @param player The player to open the screen for + * @param pos The position to open the screen at + */ default void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { PacketDistributor.PLAYER.with(player).send(new OpenScreen(DataType.SET_PASSCODE, pos)); } @@ -88,14 +92,14 @@ public default void useCodebreaker(Player player) { } /** - * Called whenever a player correctly enters this block's passcode in the passcode GUI. + * Called whenever a player correctly enters this object's passcode in the passcode GUI. * * @param player The player who entered the passcode. */ public void activate(Player player); /** - * Returns the block entity's passcode. + * Returns the object's passcode. * * @return The passcode, null if the passcode is not set yet */ @@ -153,7 +157,7 @@ default void loadSaltKey(CompoundTag tag) { default void loadPasscode(CompoundTag tag) { String passcode = tag.getString(tag.contains("Passcode", Tag.TAG_STRING) ? "Passcode" : "passcode"); //"Passcode" is also checked in order to support old versions where both spellings were used to store passcode information - //SecurityCraft's passcode-protected blocks do not support passcodes longer than 20 characters, so if such a short passcode is encountered instead of a hash, store the properly hashed version inside the block + //SecurityCraft's passcode-protected blocks did not support passcodes longer than 20 characters, so if such a short passcode is encountered instead of a hash, store the properly hashed version inside the block if (!passcode.isEmpty()) { if (passcode.length() <= 20) hashAndSetPasscode(PasscodeUtils.hashPasscodeWithoutSalt(passcode)); @@ -163,7 +167,7 @@ default void loadPasscode(CompoundTag tag) { } /** - * Returns the block entity's salt, which is used for hashing incoming passcodes. + * Returns the object'ss salt, which is used for hashing incoming passcodes. * * @return The salt, null if the passcode and salt are not set yet */ @@ -172,29 +176,29 @@ default byte[] getSalt() { } /** - * Returns the block entity's salt key, which is used for retrieving the salt from the external salt list. + * Returns the object's salt key, which is used for retrieving the salt from the external salt list. * * @return The stored salt key, null if the passcode and salt are not set yet */ public UUID getSaltKey(); /** - * Sets the block entity's salt key, which is used for retrieving the salt from the external salt list. The salt key should - * always be set alongside the passcode. + * Sets the object's salt key, which is used for retrieving the salt from the external salt list. The salt key should always + * be set alongside the passcode. * * @param saltKey The new key associated with the salt */ public void setSaltKey(UUID saltKey); /** - * Sets this block to be on cooldown and starts the cooldown + * Sets this object to be on cooldown and starts the cooldown */ public void startCooldown(); /** - * Checks whether this block is on cooldown, meaning a new code cannot be entered. + * Checks whether this object is on cooldown, meaning a new code cannot be entered. * - * @return true if this block is on cooldown, false otherwise + * @return true if this object is on cooldown, false otherwise */ public boolean isOnCooldown(); From 242d397de01987bec64f4aa10f80722981601615 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 14:34:30 +0200 Subject: [PATCH 27/55] add boat variants --- .../models/item/acacia_security_sea_boat.json | 6 ++++ .../models/item/bamboo_security_sea_raft.json | 6 ++++ .../models/item/birch_security_sea_boat.json | 6 ++++ .../models/item/cherry_security_sea_boat.json | 6 ++++ .../item/dark_oak_security_sea_boat.json | 6 ++++ .../models/item/jungle_security_sea_boat.json | 6 ++++ .../item/mangrove_security_sea_boat.json | 6 ++++ ...a_raft.json => oak_security_sea_boat.json} | 2 +- .../models/item/spruce_security_sea_boat.json | 6 ++++ .../acacia_security_sea_boat.json | 34 ++++++++++++++++++ .../bamboo_security_sea_raft.json | 34 ++++++++++++++++++ .../birch_security_sea_boat.json | 34 ++++++++++++++++++ .../cherry_security_sea_boat.json | 34 ++++++++++++++++++ .../dark_oak_security_sea_boat.json | 34 ++++++++++++++++++ .../jungle_security_sea_boat.json | 34 ++++++++++++++++++ .../mangrove_security_sea_boat.json | 34 ++++++++++++++++++ ...a_raft.json => oak_security_sea_boat.json} | 4 +-- .../spruce_security_sea_boat.json | 34 ++++++++++++++++++ .../recipes/acacia_security_sea_boat.json | 20 +++++++++++ ...aft.json => bamboo_security_sea_raft.json} | 2 +- .../recipes/birch_security_sea_boat.json | 20 +++++++++++ .../recipes/cherry_security_sea_boat.json | 20 +++++++++++ .../recipes/dark_oak_security_sea_boat.json | 20 +++++++++++ .../recipes/jungle_security_sea_boat.json | 20 +++++++++++ .../recipes/mangrove_security_sea_boat.json | 20 +++++++++++ .../recipes/oak_security_sea_boat.json | 20 +++++++++++ .../recipes/spruce_security_sea_boat.json | 20 +++++++++++ .../securitycraft/ClientHandler.java | 4 +-- .../securitycraft/RegistrationHandler.java | 11 ++++++ .../geforcemods/securitycraft/SCContent.java | 31 ++++++++++++---- .../securitycraft/SCCreativeModeTabs.java | 10 +++++- .../datagen/RecipeGenerator.java | 10 +++++- ...uritySeaRaft.java => SecuritySeaBoat.java} | 24 +++++++++---- ...RaftItem.java => SecuritySeaBoatItem.java} | 16 ++++----- .../securitycraft/misc/PageGroup.java | 3 +- .../renderers/SecuritySeaBoatRenderer.java | 23 ++++++++++++ .../renderers/SecuritySeaRaftRenderer.java | 26 -------------- .../resources/META-INF/accesstransformer.cfg | 4 ++- .../assets/securitycraft/lang/de_at.json | 13 +++++-- .../assets/securitycraft/lang/de_ch.json | 13 +++++-- .../assets/securitycraft/lang/de_de.json | 13 +++++-- .../assets/securitycraft/lang/en_us.json | 13 +++++-- .../entity/security_sea_boat/acacia.png | Bin 0 -> 4081 bytes .../bamboo.png} | Bin .../entity/security_sea_boat/birch.png | Bin 0 -> 4053 bytes .../entity/security_sea_boat/cherry.png | Bin 0 -> 4190 bytes .../entity/security_sea_boat/dark_oak.png | Bin 0 -> 4033 bytes .../entity/security_sea_boat/jungle.png | Bin 0 -> 4088 bytes .../entity/security_sea_boat/mangrove.png | Bin 0 -> 4087 bytes .../textures/entity/security_sea_boat/oak.png | Bin 0 -> 4111 bytes .../entity/security_sea_boat/spruce.png | Bin 0 -> 4046 bytes .../item/acacia_security_sea_boat.png | Bin 0 -> 325 bytes ..._raft.png => bamboo_security_sea_raft.png} | Bin .../textures/item/birch_security_sea_boat.png | Bin 0 -> 325 bytes .../item/cherry_security_sea_boat.png | Bin 0 -> 330 bytes .../item/dark_oak_security_sea_boat.png | Bin 0 -> 325 bytes .../item/jungle_security_sea_boat.png | Bin 0 -> 325 bytes .../item/mangrove_security_sea_boat.png | Bin 0 -> 325 bytes .../textures/item/oak_security_sea_boat.png | Bin 0 -> 325 bytes .../item/spruce_security_sea_boat.png | Bin 0 -> 325 bytes 60 files changed, 636 insertions(+), 66 deletions(-) create mode 100644 src/generated/resources/assets/securitycraft/models/item/acacia_security_sea_boat.json create mode 100644 src/generated/resources/assets/securitycraft/models/item/bamboo_security_sea_raft.json create mode 100644 src/generated/resources/assets/securitycraft/models/item/birch_security_sea_boat.json create mode 100644 src/generated/resources/assets/securitycraft/models/item/cherry_security_sea_boat.json create mode 100644 src/generated/resources/assets/securitycraft/models/item/dark_oak_security_sea_boat.json create mode 100644 src/generated/resources/assets/securitycraft/models/item/jungle_security_sea_boat.json create mode 100644 src/generated/resources/assets/securitycraft/models/item/mangrove_security_sea_boat.json rename src/generated/resources/assets/securitycraft/models/item/{security_sea_raft.json => oak_security_sea_boat.json} (52%) create mode 100644 src/generated/resources/assets/securitycraft/models/item/spruce_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/acacia_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/bamboo_security_sea_raft.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/birch_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/cherry_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/dark_oak_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/jungle_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/mangrove_security_sea_boat.json rename src/generated/resources/data/securitycraft/advancements/recipes/transportation/{security_sea_raft.json => oak_security_sea_boat.json} (84%) create mode 100644 src/generated/resources/data/securitycraft/advancements/recipes/transportation/spruce_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/acacia_security_sea_boat.json rename src/generated/resources/data/securitycraft/recipes/{security_sea_raft.json => bamboo_security_sea_raft.json} (85%) create mode 100644 src/generated/resources/data/securitycraft/recipes/birch_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/cherry_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/dark_oak_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/jungle_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/mangrove_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/oak_security_sea_boat.json create mode 100644 src/generated/resources/data/securitycraft/recipes/spruce_security_sea_boat.json rename src/main/java/net/geforcemods/securitycraft/entity/{SecuritySeaRaft.java => SecuritySeaBoat.java} (88%) rename src/main/java/net/geforcemods/securitycraft/items/{SecuritySeaRaftItem.java => SecuritySeaBoatItem.java} (80%) create mode 100644 src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java delete mode 100644 src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/acacia.png rename src/main/resources/assets/securitycraft/textures/entity/{security_sea_raft.png => security_sea_boat/bamboo.png} (100%) create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/birch.png create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/cherry.png create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/dark_oak.png create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/jungle.png create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/mangrove.png create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/oak.png create mode 100644 src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/spruce.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/acacia_security_sea_boat.png rename src/main/resources/assets/securitycraft/textures/item/{security_sea_raft.png => bamboo_security_sea_raft.png} (100%) create mode 100644 src/main/resources/assets/securitycraft/textures/item/birch_security_sea_boat.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/cherry_security_sea_boat.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/dark_oak_security_sea_boat.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/jungle_security_sea_boat.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/mangrove_security_sea_boat.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/oak_security_sea_boat.png create mode 100644 src/main/resources/assets/securitycraft/textures/item/spruce_security_sea_boat.png diff --git a/src/generated/resources/assets/securitycraft/models/item/acacia_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/acacia_security_sea_boat.json new file mode 100644 index 0000000000..a9b38a3788 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/acacia_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/acacia_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/bamboo_security_sea_raft.json b/src/generated/resources/assets/securitycraft/models/item/bamboo_security_sea_raft.json new file mode 100644 index 0000000000..11edb63161 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/bamboo_security_sea_raft.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/bamboo_security_sea_raft" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/birch_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/birch_security_sea_boat.json new file mode 100644 index 0000000000..d6650d3a2d --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/birch_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/birch_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/cherry_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/cherry_security_sea_boat.json new file mode 100644 index 0000000000..65132edce1 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/cherry_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/cherry_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/dark_oak_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/dark_oak_security_sea_boat.json new file mode 100644 index 0000000000..3245194b7d --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/dark_oak_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/dark_oak_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/jungle_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/jungle_security_sea_boat.json new file mode 100644 index 0000000000..4b6e91d8a3 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/jungle_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/jungle_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/mangrove_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/mangrove_security_sea_boat.json new file mode 100644 index 0000000000..f9ac183b72 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/mangrove_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/mangrove_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json b/src/generated/resources/assets/securitycraft/models/item/oak_security_sea_boat.json similarity index 52% rename from src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json rename to src/generated/resources/assets/securitycraft/models/item/oak_security_sea_boat.json index 07f352def0..2a53a99c48 100644 --- a/src/generated/resources/assets/securitycraft/models/item/security_sea_raft.json +++ b/src/generated/resources/assets/securitycraft/models/item/oak_security_sea_boat.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "securitycraft:item/security_sea_raft" + "layer0": "securitycraft:item/oak_security_sea_boat" } } \ No newline at end of file diff --git a/src/generated/resources/assets/securitycraft/models/item/spruce_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/spruce_security_sea_boat.json new file mode 100644 index 0000000000..b92d3601ef --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/spruce_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "securitycraft:item/spruce_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/acacia_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/acacia_security_sea_boat.json new file mode 100644 index 0000000000..060028e3b7 --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/acacia_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:acacia_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:acacia_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/bamboo_security_sea_raft.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/bamboo_security_sea_raft.json new file mode 100644 index 0000000000..d946519811 --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/bamboo_security_sea_raft.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:bamboo_security_sea_raft" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:bamboo_security_sea_raft" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/birch_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/birch_security_sea_boat.json new file mode 100644 index 0000000000..27b48bbdec --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/birch_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:birch_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:birch_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/cherry_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/cherry_security_sea_boat.json new file mode 100644 index 0000000000..1b7bbba54c --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/cherry_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:cherry_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:cherry_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/dark_oak_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/dark_oak_security_sea_boat.json new file mode 100644 index 0000000000..a8f270dabf --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/dark_oak_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:dark_oak_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:dark_oak_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/jungle_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/jungle_security_sea_boat.json new file mode 100644 index 0000000000..92a9c007de --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/jungle_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:jungle_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:jungle_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/mangrove_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/mangrove_security_sea_boat.json new file mode 100644 index 0000000000..31765026e7 --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/mangrove_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:mangrove_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:mangrove_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/oak_security_sea_boat.json similarity index 84% rename from src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json rename to src/generated/resources/data/securitycraft/advancements/recipes/transportation/oak_security_sea_boat.json index f03c8feea1..0216693a12 100644 --- a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/security_sea_raft.json +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/oak_security_sea_boat.json @@ -15,7 +15,7 @@ }, "has_the_recipe": { "conditions": { - "recipe": "securitycraft:security_sea_raft" + "recipe": "securitycraft:oak_security_sea_boat" }, "trigger": "minecraft:recipe_unlocked" } @@ -28,7 +28,7 @@ ], "rewards": { "recipes": [ - "securitycraft:security_sea_raft" + "securitycraft:oak_security_sea_boat" ] } } \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/advancements/recipes/transportation/spruce_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/spruce_security_sea_boat.json new file mode 100644 index 0000000000..251bdba367 --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/spruce_security_sea_boat.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_keypad_chest": { + "conditions": { + "items": [ + { + "items": [ + "securitycraft:keypad_chest" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "securitycraft:spruce_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "securitycraft:spruce_security_sea_boat" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/acacia_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/acacia_security_sea_boat.json new file mode 100644 index 0000000000..33083d2590 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/acacia_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_acacia_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:acacia_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json b/src/generated/resources/data/securitycraft/recipes/bamboo_security_sea_raft.json similarity index 85% rename from src/generated/resources/data/securitycraft/recipes/security_sea_raft.json rename to src/generated/resources/data/securitycraft/recipes/bamboo_security_sea_raft.json index 9da0f0556c..f9f79f81b6 100644 --- a/src/generated/resources/data/securitycraft/recipes/security_sea_raft.json +++ b/src/generated/resources/data/securitycraft/recipes/bamboo_security_sea_raft.json @@ -15,6 +15,6 @@ "PPP" ], "result": { - "item": "securitycraft:security_sea_raft" + "item": "securitycraft:bamboo_security_sea_raft" } } \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/birch_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/birch_security_sea_boat.json new file mode 100644 index 0000000000..0ca45e0e53 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/birch_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_birch_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:birch_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/cherry_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/cherry_security_sea_boat.json new file mode 100644 index 0000000000..ded096e997 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/cherry_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_cherry_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:cherry_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/dark_oak_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/dark_oak_security_sea_boat.json new file mode 100644 index 0000000000..0c81799590 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/dark_oak_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_dark_oak_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:dark_oak_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/jungle_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/jungle_security_sea_boat.json new file mode 100644 index 0000000000..29d1800781 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/jungle_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_jungle_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:jungle_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/mangrove_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/mangrove_security_sea_boat.json new file mode 100644 index 0000000000..0c9ba32b13 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/mangrove_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_mangrove_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:mangrove_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/oak_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/oak_security_sea_boat.json new file mode 100644 index 0000000000..88164266fb --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/oak_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_oak_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:oak_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/securitycraft/recipes/spruce_security_sea_boat.json b/src/generated/resources/data/securitycraft/recipes/spruce_security_sea_boat.json new file mode 100644 index 0000000000..e29565b65a --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/spruce_security_sea_boat.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "securitycraft:security_sea_boats", + "key": { + "C": { + "item": "securitycraft:keypad_chest" + }, + "P": { + "item": "securitycraft:reinforced_spruce_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "item": "securitycraft:spruce_security_sea_boat" + } +} \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index 99e7df8418..1940f4abb8 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -62,7 +62,7 @@ import net.geforcemods.securitycraft.renderers.SecretHangingSignRenderer; import net.geforcemods.securitycraft.renderers.SecretSignRenderer; import net.geforcemods.securitycraft.renderers.SecurityCameraRenderer; -import net.geforcemods.securitycraft.renderers.SecuritySeaRaftRenderer; +import net.geforcemods.securitycraft.renderers.SecuritySeaBoatRenderer; import net.geforcemods.securitycraft.renderers.SentryRenderer; import net.geforcemods.securitycraft.renderers.SonicSecuritySystemRenderer; import net.geforcemods.securitycraft.renderers.TrophySystemRenderer; @@ -417,7 +417,7 @@ public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderer event.registerEntityRenderer(SCContent.SECURITY_CAMERA_ENTITY.get(), NoopRenderer::new); event.registerEntityRenderer(SCContent.SENTRY_ENTITY.get(), SentryRenderer::new); event.registerEntityRenderer(SCContent.BULLET_ENTITY.get(), BulletRenderer::new); - event.registerEntityRenderer(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), SecuritySeaRaftRenderer::new); + event.registerEntityRenderer(SCContent.SECURITY_SEA_BOAT_ENTITY.get(), SecuritySeaBoatRenderer::new); //normal renderers event.registerBlockEntityRenderer(SCContent.BLOCK_POCKET_MANAGER_BLOCK_ENTITY.get(), BlockPocketManagerRenderer::new); event.registerBlockEntityRenderer(SCContent.CLAYMORE_BLOCK_ENTITY.get(), ClaymoreRenderer::new); diff --git a/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java b/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java index a0bc587b12..0399d8381f 100644 --- a/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java @@ -394,6 +394,17 @@ else if (tabKey.equals(CreativeModeTabs.OP_BLOCKS)) { entries.put(new ItemStack(SCContent.ADMIN_TOOL.get()), TabVisibility.PARENT_AND_SEARCH_TABS); entries.put(new ItemStack(SCContent.CODEBREAKER.get()), TabVisibility.PARENT_AND_SEARCH_TABS); } + else if (tabKey.equals(CreativeModeTabs.TOOLS_AND_UTILITIES)) { + entries.putAfter(new ItemStack(Items.OAK_CHEST_BOAT), new ItemStack(SCContent.OAK_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.SPRUCE_CHEST_BOAT), new ItemStack(SCContent.SPRUCE_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.BIRCH_CHEST_BOAT), new ItemStack(SCContent.BIRCH_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.JUNGLE_CHEST_BOAT), new ItemStack(SCContent.JUNGLE_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.ACACIA_CHEST_BOAT), new ItemStack(SCContent.ACACIA_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.DARK_OAK_CHEST_BOAT), new ItemStack(SCContent.DARK_OAK_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.MANGROVE_CHEST_BOAT), new ItemStack(SCContent.MANGROVE_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.CHERRY_CHEST_BOAT), new ItemStack(SCContent.CHERRY_SECURITY_SEA_BOAT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + entries.putAfter(new ItemStack(Items.BAMBOO_CHEST_RAFT), new ItemStack(SCContent.BAMBOO_SECURITY_SEA_RAFT.get()), TabVisibility.PARENT_AND_SEARCH_TABS); + } } public static void registerFakeLiquidRecipes() { diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index a63a8499a9..3afeaed90f 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -178,7 +178,7 @@ import net.geforcemods.securitycraft.commands.SingleGameProfileArgument; import net.geforcemods.securitycraft.entity.BouncingBetty; import net.geforcemods.securitycraft.entity.IMSBomb; -import net.geforcemods.securitycraft.entity.SecuritySeaRaft; +import net.geforcemods.securitycraft.entity.SecuritySeaBoat; import net.geforcemods.securitycraft.entity.camera.SecurityCamera; import net.geforcemods.securitycraft.entity.sentry.Bullet; import net.geforcemods.securitycraft.entity.sentry.Sentry; @@ -218,7 +218,7 @@ import net.geforcemods.securitycraft.items.ModuleItem; import net.geforcemods.securitycraft.items.PortableTunePlayerItem; import net.geforcemods.securitycraft.items.SCManualItem; -import net.geforcemods.securitycraft.items.SecuritySeaRaftItem; +import net.geforcemods.securitycraft.items.SecuritySeaBoatItem; import net.geforcemods.securitycraft.items.SentryItem; import net.geforcemods.securitycraft.items.SentryRemoteAccessToolItem; import net.geforcemods.securitycraft.items.SonicSecuritySystemItem; @@ -252,6 +252,7 @@ import net.minecraft.network.syncher.EntityDataSerializer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.flag.FeatureFlag; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.BlockItem; @@ -2630,8 +2631,24 @@ public class SCContent { public static final DeferredItem SECRET_CRIMSON_HANGING_SIGN_ITEM = ITEMS.register("secret_crimson_hanging_sign", () -> new HangingSignItem(SCContent.SECRET_CRIMSON_HANGING_SIGN.get(), SCContent.SECRET_CRIMSON_WALL_HANGING_SIGN.get(), itemProp(16))); @HasManualPage(PageGroup.SECRET_HANGING_SIGNS) public static final DeferredItem SECRET_WARPED_HANGING_SIGN_ITEM = ITEMS.register("secret_warped_hanging_sign", () -> new HangingSignItem(SCContent.SECRET_WARPED_HANGING_SIGN.get(), SCContent.SECRET_WARPED_WALL_HANGING_SIGN.get(), itemProp(16))); - @HasManualPage - public static final DeferredItem SECURITY_SEA_RAFT_ITEM = ITEMS.register("security_sea_raft", () -> new SecuritySeaRaftItem(itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem OAK_SECURITY_SEA_BOAT = ITEMS.register("oak_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.OAK, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem SPRUCE_SECURITY_SEA_BOAT = ITEMS.register("spruce_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.SPRUCE, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem BIRCH_SECURITY_SEA_BOAT = ITEMS.register("birch_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.BIRCH, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem JUNGLE_SECURITY_SEA_BOAT = ITEMS.register("jungle_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.JUNGLE, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem ACACIA_SECURITY_SEA_BOAT = ITEMS.register("acacia_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.ACACIA, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem DARK_OAK_SECURITY_SEA_BOAT = ITEMS.register("dark_oak_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.DARK_OAK, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem MANGROVE_SECURITY_SEA_BOAT = ITEMS.register("mangrove_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.MANGROVE, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem CHERRY_SECURITY_SEA_BOAT = ITEMS.register("cherry_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.CHERRY, itemProp(1))); + @HasManualPage(PageGroup.SECURITY_SEA_BOATS) + public static final DeferredItem BAMBOO_SECURITY_SEA_RAFT = ITEMS.register("bamboo_security_sea_raft", () -> new SecuritySeaBoatItem(Boat.Type.BAMBOO, itemProp(1))); @HasManualPage(designedBy = "Henzoid") public static final DeferredItem SENTRY = ITEMS.register("sentry", () -> new SentryItem(itemProp())); public static final DeferredItem SONIC_SECURITY_SYSTEM_ITEM = ITEMS.register("sonic_security_system", () -> new SonicSecuritySystemItem(itemProp(1))); @@ -2922,12 +2939,12 @@ else if (state.is(BOUNCING_BETTY)) .setUpdateInterval(1) .setShouldReceiveVelocityUpdates(true) .build(SecurityCraft.MODID + ":bullet")); - public static final DeferredHolder, EntityType> SECURITY_SEA_RAFT_ENTITY = ENTITY_TYPES.register("security_sea_raft", - () -> EntityType.Builder.of(SecuritySeaRaft::new, MobCategory.MISC) + public static final DeferredHolder, EntityType> SECURITY_SEA_BOAT_ENTITY = ENTITY_TYPES.register("security_sea_boat", + () -> EntityType.Builder.of(SecuritySeaBoat::new, MobCategory.MISC) .sized(1.375F, 0.5625F) .clientTrackingRange(10) .fireImmune() - .build(SecurityCraft.MODID + ":security_sea_raft")); + .build(SecurityCraft.MODID + ":security_sea_boat")); //@formatter:on //container types diff --git a/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java b/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java index 948eeda32b..dde36f3e84 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java +++ b/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java @@ -131,7 +131,15 @@ public class SCCreativeModeTabs { output.accept(new ItemStack(SCContent.FAKE_WATER_BUCKET.get())); output.accept(new ItemStack(SCContent.FAKE_LAVA_BUCKET.get())); output.accept(new ItemStack(SCContent.ADMIN_TOOL.get())); - output.accept(new ItemStack(SCContent.SECURITY_SEA_RAFT_ITEM.get())); + output.accept(new ItemStack(SCContent.OAK_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.SPRUCE_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.BIRCH_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.JUNGLE_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.ACACIA_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.DARK_OAK_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.MANGROVE_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.CHERRY_SECURITY_SEA_BOAT.get())); + output.accept(new ItemStack(SCContent.BAMBOO_SECURITY_SEA_RAFT.get())); output.acceptAll(STACKS_FOR_ITEM_GROUPS.get(SCItemGroup.TECHNICAL)); }).build()); //@formatter:off diff --git a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java index db7392420c..d7a3d2ef38 100644 --- a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java +++ b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java @@ -881,7 +881,15 @@ protected final void buildRecipes(RecipeOutput recipeOutput) { addSecretSignRecipe(recipeOutput, Items.OAK_HANGING_SIGN, SCContent.SECRET_OAK_HANGING_SIGN.get()); addSecretSignRecipe(recipeOutput, Items.SPRUCE_HANGING_SIGN, SCContent.SECRET_SPRUCE_HANGING_SIGN.get()); addSecretSignRecipe(recipeOutput, Items.WARPED_HANGING_SIGN, SCContent.SECRET_WARPED_HANGING_SIGN.get()); - addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_BAMBOO_PLANKS.get(), SCContent.SECURITY_SEA_RAFT_ITEM.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_OAK_PLANKS.get(), SCContent.OAK_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_SPRUCE_PLANKS.get(), SCContent.SPRUCE_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_BIRCH_PLANKS.get(), SCContent.BIRCH_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_JUNGLE_PLANKS.get(), SCContent.JUNGLE_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_ACACIA_PLANKS.get(), SCContent.ACACIA_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_DARK_OAK_PLANKS.get(), SCContent.DARK_OAK_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_MANGROVE_PLANKS.get(), SCContent.MANGROVE_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_CHERRY_PLANKS.get(), SCContent.CHERRY_SECURITY_SEA_BOAT.get()); + addSecuritySeaBoatRecipe(recipeOutput, SCContent.REINFORCED_BAMBOO_PLANKS.get(), SCContent.BAMBOO_SECURITY_SEA_RAFT.get()); addSlabRecipe(recipeOutput, Ingredient.of(SCContent.CRYSTAL_QUARTZ_BLOCK.get(), SCContent.CRYSTAL_QUARTZ_PILLAR.get(), SCContent.CHISELED_CRYSTAL_QUARTZ.get()), SCContent.CRYSTAL_QUARTZ_SLAB.get()); addSlabRecipe(recipeOutput, SCContent.REINFORCED_ACACIA_PLANKS.get(), SCContent.REINFORCED_ACACIA_SLAB.get()); addSlabRecipe(recipeOutput, SCContent.REINFORCED_ANDESITE.get(), SCContent.REINFORCED_ANDESITE_SLAB.get()); diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java similarity index 88% rename from src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java rename to src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 35a826324e..1a86bb405e 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaRaft.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -33,17 +33,17 @@ import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; -public class SecuritySeaRaft extends ChestBoat implements IOwnable, IPasscodeProtected { - private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaRaft.class, Owner.getSerializer()); +public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodeProtected { + private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaBoat.class, Owner.getSerializer()); private byte[] passcode; private UUID saltKey; - public SecuritySeaRaft(EntityType type, Level level) { - super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); + public SecuritySeaBoat(EntityType type, Level level) { + super(SCContent.SECURITY_SEA_BOAT_ENTITY.get(), level); } - public SecuritySeaRaft(Level level, double x, double y, double z) { - super(SCContent.SECURITY_SEA_RAFT_ENTITY.get(), level); + public SecuritySeaBoat(Level level, double x, double y, double z) { + super(SCContent.SECURITY_SEA_BOAT_ENTITY.get(), level); setPos(x, y, z); xo = y; yo = y; @@ -222,6 +222,16 @@ public long getCooldownEnd() { @Override public Item getDropItem() { - return SCContent.SECURITY_SEA_RAFT_ITEM.get(); + return (switch (getVariant()) { + case SPRUCE -> SCContent.SPRUCE_SECURITY_SEA_BOAT; + case BIRCH -> SCContent.BIRCH_SECURITY_SEA_BOAT; + case JUNGLE -> SCContent.JUNGLE_SECURITY_SEA_BOAT; + case ACACIA -> SCContent.ACACIA_SECURITY_SEA_BOAT; + case DARK_OAK -> SCContent.DARK_OAK_SECURITY_SEA_BOAT; + case MANGROVE -> SCContent.MANGROVE_SECURITY_SEA_BOAT; + case CHERRY -> SCContent.CHERRY_SECURITY_SEA_BOAT; + case BAMBOO -> SCContent.BAMBOO_SECURITY_SEA_RAFT; + default -> SCContent.OAK_SECURITY_SEA_BOAT; + }).get(); } } diff --git a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaBoatItem.java similarity index 80% rename from src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java rename to src/main/java/net/geforcemods/securitycraft/items/SecuritySeaBoatItem.java index 5ac27bf6da..785c7574cd 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaRaftItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaBoatItem.java @@ -1,6 +1,6 @@ package net.geforcemods.securitycraft.items; -import net.geforcemods.securitycraft.entity.SecuritySeaRaft; +import net.geforcemods.securitycraft.entity.SecuritySeaBoat; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.FluidTags; import net.minecraft.world.InteractionHand; @@ -17,9 +17,9 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -public class SecuritySeaRaftItem extends BoatItem { - public SecuritySeaRaftItem(Item.Properties properties) { - super(true, Boat.Type.BAMBOO, properties); +public class SecuritySeaBoatItem extends BoatItem { + public SecuritySeaBoatItem(Boat.Type type, Item.Properties properties) { + super(true, type, properties); } @Override @@ -35,12 +35,12 @@ public InteractionResultHolder use(Level level, Player player, Intera @Override public Boat getBoat(Level level, HitResult hitResult, ItemStack stack, Player player) { Vec3 vec3 = hitResult.getLocation(); - SecuritySeaRaft raft = new SecuritySeaRaft(level, vec3.x, vec3.y, vec3.z); + SecuritySeaBoat boat = new SecuritySeaBoat(level, vec3.x, vec3.y, vec3.z); if (level instanceof ServerLevel serverLevel) - EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(raft); + EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(boat); - raft.setOwner(player); - return raft; + boat.setOwner(player); + return boat; } } diff --git a/src/main/java/net/geforcemods/securitycraft/misc/PageGroup.java b/src/main/java/net/geforcemods/securitycraft/misc/PageGroup.java index d74734bd35..6ee8c77f20 100644 --- a/src/main/java/net/geforcemods/securitycraft/misc/PageGroup.java +++ b/src/main/java/net/geforcemods/securitycraft/misc/PageGroup.java @@ -14,7 +14,8 @@ public enum PageGroup { SECRET_HANGING_SIGNS(true, "gui.securitycraft:scManual.secret_hanging_signs", "securitycraft.secret_signs.info"), BLOCK_REINFORCERS(true, "gui.securitycraft:scManual.block_reinforcers", "securitycraft.block_reinforcers.info"), DISPLAY_CASES(true, "gui.securitycraft:scManual.display_cases", "securitycraft.display_cases.info"), - FENCE_GATES(true, "gui.securitycraft:scManual.reinforced_fence_gates", "securitycraft.reinforced_fence_gates.info"); + FENCE_GATES(true, "gui.securitycraft:scManual.reinforced_fence_gates", "securitycraft.reinforced_fence_gates.info"), + SECURITY_SEA_BOATS(true, "gui.securitycraft:scManual.security_sea_boats", "securitycraft.security_sea_boats.info"); private final boolean hasRecipeGrid; private final String title; diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java new file mode 100644 index 0000000000..e41e968069 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java @@ -0,0 +1,23 @@ +package net.geforcemods.securitycraft.renderers; + +import java.util.stream.Stream; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; + +import net.geforcemods.securitycraft.SecurityCraft; +import net.minecraft.client.renderer.entity.BoatRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.vehicle.Boat; + +public class SecuritySeaBoatRenderer extends BoatRenderer { + public SecuritySeaBoatRenderer(EntityRendererProvider.Context ctx) { + super(ctx, true); + //@formatter:off + boatResources = Stream.of(Boat.Type.values()) + .collect(ImmutableMap.toImmutableMap( + type -> type, + type -> Pair.of(new ResourceLocation(SecurityCraft.MODID, "textures/entity/security_sea_boat/" + type.getName() + ".png"), createBoatModel(ctx, type, true)))); + } +} diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java deleted file mode 100644 index 4e9a52e94f..0000000000 --- a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftRenderer.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.geforcemods.securitycraft.renderers; - -import com.mojang.datafixers.util.Pair; - -import net.geforcemods.securitycraft.SecurityCraft; -import net.minecraft.client.model.ChestRaftModel; -import net.minecraft.client.model.ListModel; -import net.minecraft.client.model.geom.ModelLayers; -import net.minecraft.client.renderer.entity.BoatRenderer; -import net.minecraft.client.renderer.entity.EntityRendererProvider; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.vehicle.Boat; - -public class SecuritySeaRaftRenderer extends BoatRenderer { - private final Pair> textureAndModel; - - public SecuritySeaRaftRenderer(EntityRendererProvider.Context ctx) { - super(ctx, true); - textureAndModel = new Pair<>(new ResourceLocation(SecurityCraft.MODID, "textures/entity/security_sea_raft.png"), new ChestRaftModel(ctx.bakeLayer(ModelLayers.createChestBoatModelName(Boat.Type.BAMBOO)))); - } - - @Override - public Pair> getModelWithLocation(Boat boat) { - return textureAndModel; - } -} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 033936a51a..8a6c9f0abb 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -66,4 +66,6 @@ public net.minecraft.client.gui.screens.inventory.BookViewScreen forwardButton public net.minecraft.client.gui.screens.inventory.BookViewScreen backButton public net.minecraft.client.gui.screens.inventory.BookViewScreen updateButtonVisibility()V public net.minecraft.server.commands.FillCommand ERROR_AREA_TOO_LARGE -public net.minecraft.world.item.BoatItem getBoat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/phys/HitResult;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/player/Player;)Lnet/minecraft/world/entity/vehicle/Boat; \ No newline at end of file +public net.minecraft.world.item.BoatItem getBoat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/phys/HitResult;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/player/Player;)Lnet/minecraft/world/entity/vehicle/Boat; +public-f net.minecraft.client.renderer.entity.BoatRenderer boatResources +public net.minecraft.client.renderer.entity.BoatRenderer createBoatModel(Lnet/minecraft/client/renderer/entity/EntityRendererProvider$Context;Lnet/minecraft/world/entity/vehicle/Boat$Type;Z)Lnet/minecraft/client/model/ListModel; diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index 4e4adc3207..20ebc09f4e 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -789,6 +789,7 @@ "gui.securitycraft:scManual.reinforced_pressure_plates": "Verstärkte Druckplatten", "gui.securitycraft:scManual.secret_hanging_signs": "Geheime Hängeschilder", "gui.securitycraft:scManual.secret_signs": "Geheime Schilder", + "gui.securitycraft:scManual.security_sea_boats": "Gesicherte Seeboote", "gui.securitycraft:scManual.viewActivatedBlock": "Sichtfeldaktivierung: Dieser Block wird aktiviert, indem du ihn mit deinem Fadenkreuz anschaust.", "gui.securitycraft:sonic_security_system.invert.tooltip_default": "Erlaubt momentan den Zugriff auf Blöcke, sobald die richtige Melodie gespielt wird.", "gui.securitycraft:sonic_security_system.invert.tooltip_inverted": "Verhindert momentan den Zugriff auf Blöcke, sobald die richtige Melodie gespielt wird.", @@ -896,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann jedoch nicht auf Lava schwimmen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -912,18 +913,24 @@ "help.securitycraft.username_logger.info": "Der Nutzernamen-Protokollierer protokolliert jeden Spieler, der sich ihm in einem Radius von 3 Blöcken nähert, wenn er mit Redstone gepowert ist.", "help.securitycraft.whitelist_module.info": "Das Zulassungslisten-Modul verschont zugelassene Spieler von einer Passworteingabe oder anderen Eigenschaften des Blocks.", "help.securitycraft.wire_cutters.info": "Der Drahtschneider kann genutzt werden, um mit einem Rechtsklick verschiedene Arten an Minen zu entschärfen, und die Käfigfalle zu deaktivieren. Weiterhin führt ein Schleich-Rechtsklick auf einen passwortgesicherten Block dazu, dass die Passwortsicherung entfernt wird.", + "item.securitycraft.acacia_security_sea_boat": "Gesichertes Akazienholz-Seeboot", "item.securitycraft.admin_tool": "Admin-Werkzeug", + "item.securitycraft.bamboo_security_sea_raft": "Gesichertes Bambus-Seefloß", + "item.securitycraft.birch_security_sea_boat": "Gesichertes Birkenholz-Seeboot", "item.securitycraft.blacklist_module": "Sperrlisten-Modul", "item.securitycraft.briefcase": "Aktenkoffer", "item.securitycraft.bucket_f_lava": "Eimer mit falscher Lava", "item.securitycraft.bucket_f_water": "Eimer mit falschem Wasser", "item.securitycraft.camera_monitor": "Kameramonitor", + "item.securitycraft.cherry_security_sea_boat": "Gesichertes Kirschenholz-Seeboot", "item.securitycraft.codebreaker": "Codebrecher", "item.securitycraft.colored_lens": "Gefärbte Linse", "item.securitycraft.crystal_quartz_item": "Kristallquarz", + "item.securitycraft.dark_oak_security_sea_boat": "Gesichertes Schwarzeichenholz-Seeboot", "item.securitycraft.disguise_module": "Tarnmodul", "item.securitycraft.door_indestructible_iron_item": "Verstärkte Eisentür", "item.securitycraft.harming_module": "Schadensmodul", + "item.securitycraft.jungle_security_sea_boat": "Gesichertes Tropenholz-Seeboot", "item.securitycraft.keycard_holder": "Schlüsselkartenhalter", "item.securitycraft.keycard_lv1": "Level 1 Schlüsselkarte", "item.securitycraft.keycard_lv2": "Level 2 Schlüsselkarte", @@ -935,16 +942,18 @@ "item.securitycraft.keypad_item": "Nummernfeld", "item.securitycraft.lens": "Linse", "item.securitycraft.limited_use_keycard": "Begrenzt nutzbare Schlüsselkarte", + "item.securitycraft.mangrove_security_sea_boat": "Gesichertes Mangrovenholz-Seeboot", + "item.securitycraft.oak_security_sea_boat": "Gesichertes Eichenholz-Seeboot", "item.securitycraft.portable_tune_player": "Tragbarer Melodienspieler", "item.securitycraft.redstone_module": "Redstone-Modul", "item.securitycraft.remote_access_mine": "Kabellose Minensteuerungseinheit", "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", - "item.securitycraft.security_sea_raft": "Gesichertes Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", + "item.securitycraft.spruce_security_sea_boat": "Gesichertes Fichtenholz-Seeboot", "item.securitycraft.storage_module": "Lagermodul", "item.securitycraft.taser": "Taser", "item.securitycraft.taser_powered": "Geladener Taser", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index 4e4adc3207..20ebc09f4e 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -789,6 +789,7 @@ "gui.securitycraft:scManual.reinforced_pressure_plates": "Verstärkte Druckplatten", "gui.securitycraft:scManual.secret_hanging_signs": "Geheime Hängeschilder", "gui.securitycraft:scManual.secret_signs": "Geheime Schilder", + "gui.securitycraft:scManual.security_sea_boats": "Gesicherte Seeboote", "gui.securitycraft:scManual.viewActivatedBlock": "Sichtfeldaktivierung: Dieser Block wird aktiviert, indem du ihn mit deinem Fadenkreuz anschaust.", "gui.securitycraft:sonic_security_system.invert.tooltip_default": "Erlaubt momentan den Zugriff auf Blöcke, sobald die richtige Melodie gespielt wird.", "gui.securitycraft:sonic_security_system.invert.tooltip_inverted": "Verhindert momentan den Zugriff auf Blöcke, sobald die richtige Melodie gespielt wird.", @@ -896,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann jedoch nicht auf Lava schwimmen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -912,18 +913,24 @@ "help.securitycraft.username_logger.info": "Der Nutzernamen-Protokollierer protokolliert jeden Spieler, der sich ihm in einem Radius von 3 Blöcken nähert, wenn er mit Redstone gepowert ist.", "help.securitycraft.whitelist_module.info": "Das Zulassungslisten-Modul verschont zugelassene Spieler von einer Passworteingabe oder anderen Eigenschaften des Blocks.", "help.securitycraft.wire_cutters.info": "Der Drahtschneider kann genutzt werden, um mit einem Rechtsklick verschiedene Arten an Minen zu entschärfen, und die Käfigfalle zu deaktivieren. Weiterhin führt ein Schleich-Rechtsklick auf einen passwortgesicherten Block dazu, dass die Passwortsicherung entfernt wird.", + "item.securitycraft.acacia_security_sea_boat": "Gesichertes Akazienholz-Seeboot", "item.securitycraft.admin_tool": "Admin-Werkzeug", + "item.securitycraft.bamboo_security_sea_raft": "Gesichertes Bambus-Seefloß", + "item.securitycraft.birch_security_sea_boat": "Gesichertes Birkenholz-Seeboot", "item.securitycraft.blacklist_module": "Sperrlisten-Modul", "item.securitycraft.briefcase": "Aktenkoffer", "item.securitycraft.bucket_f_lava": "Eimer mit falscher Lava", "item.securitycraft.bucket_f_water": "Eimer mit falschem Wasser", "item.securitycraft.camera_monitor": "Kameramonitor", + "item.securitycraft.cherry_security_sea_boat": "Gesichertes Kirschenholz-Seeboot", "item.securitycraft.codebreaker": "Codebrecher", "item.securitycraft.colored_lens": "Gefärbte Linse", "item.securitycraft.crystal_quartz_item": "Kristallquarz", + "item.securitycraft.dark_oak_security_sea_boat": "Gesichertes Schwarzeichenholz-Seeboot", "item.securitycraft.disguise_module": "Tarnmodul", "item.securitycraft.door_indestructible_iron_item": "Verstärkte Eisentür", "item.securitycraft.harming_module": "Schadensmodul", + "item.securitycraft.jungle_security_sea_boat": "Gesichertes Tropenholz-Seeboot", "item.securitycraft.keycard_holder": "Schlüsselkartenhalter", "item.securitycraft.keycard_lv1": "Level 1 Schlüsselkarte", "item.securitycraft.keycard_lv2": "Level 2 Schlüsselkarte", @@ -935,16 +942,18 @@ "item.securitycraft.keypad_item": "Nummernfeld", "item.securitycraft.lens": "Linse", "item.securitycraft.limited_use_keycard": "Begrenzt nutzbare Schlüsselkarte", + "item.securitycraft.mangrove_security_sea_boat": "Gesichertes Mangrovenholz-Seeboot", + "item.securitycraft.oak_security_sea_boat": "Gesichertes Eichenholz-Seeboot", "item.securitycraft.portable_tune_player": "Tragbarer Melodienspieler", "item.securitycraft.redstone_module": "Redstone-Modul", "item.securitycraft.remote_access_mine": "Kabellose Minensteuerungseinheit", "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", - "item.securitycraft.security_sea_raft": "Gesichertes Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", + "item.securitycraft.spruce_security_sea_boat": "Gesichertes Fichtenholz-Seeboot", "item.securitycraft.storage_module": "Lagermodul", "item.securitycraft.taser": "Taser", "item.securitycraft.taser_powered": "Geladener Taser", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index 4e4adc3207..20ebc09f4e 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -789,6 +789,7 @@ "gui.securitycraft:scManual.reinforced_pressure_plates": "Verstärkte Druckplatten", "gui.securitycraft:scManual.secret_hanging_signs": "Geheime Hängeschilder", "gui.securitycraft:scManual.secret_signs": "Geheime Schilder", + "gui.securitycraft:scManual.security_sea_boats": "Gesicherte Seeboote", "gui.securitycraft:scManual.viewActivatedBlock": "Sichtfeldaktivierung: Dieser Block wird aktiviert, indem du ihn mit deinem Fadenkreuz anschaust.", "gui.securitycraft:sonic_security_system.invert.tooltip_default": "Erlaubt momentan den Zugriff auf Blöcke, sobald die richtige Melodie gespielt wird.", "gui.securitycraft:sonic_security_system.invert.tooltip_inverted": "Verhindert momentan den Zugriff auf Blöcke, sobald die richtige Melodie gespielt wird.", @@ -896,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_raft.info": "Das gesicherte Seefloß ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen. Nur der Besitzer kann das Boot zerbrechen.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann jedoch nicht auf Lava schwimmen.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -912,18 +913,24 @@ "help.securitycraft.username_logger.info": "Der Nutzernamen-Protokollierer protokolliert jeden Spieler, der sich ihm in einem Radius von 3 Blöcken nähert, wenn er mit Redstone gepowert ist.", "help.securitycraft.whitelist_module.info": "Das Zulassungslisten-Modul verschont zugelassene Spieler von einer Passworteingabe oder anderen Eigenschaften des Blocks.", "help.securitycraft.wire_cutters.info": "Der Drahtschneider kann genutzt werden, um mit einem Rechtsklick verschiedene Arten an Minen zu entschärfen, und die Käfigfalle zu deaktivieren. Weiterhin führt ein Schleich-Rechtsklick auf einen passwortgesicherten Block dazu, dass die Passwortsicherung entfernt wird.", + "item.securitycraft.acacia_security_sea_boat": "Gesichertes Akazienholz-Seeboot", "item.securitycraft.admin_tool": "Admin-Werkzeug", + "item.securitycraft.bamboo_security_sea_raft": "Gesichertes Bambus-Seefloß", + "item.securitycraft.birch_security_sea_boat": "Gesichertes Birkenholz-Seeboot", "item.securitycraft.blacklist_module": "Sperrlisten-Modul", "item.securitycraft.briefcase": "Aktenkoffer", "item.securitycraft.bucket_f_lava": "Eimer mit falscher Lava", "item.securitycraft.bucket_f_water": "Eimer mit falschem Wasser", "item.securitycraft.camera_monitor": "Kameramonitor", + "item.securitycraft.cherry_security_sea_boat": "Gesichertes Kirschenholz-Seeboot", "item.securitycraft.codebreaker": "Codebrecher", "item.securitycraft.colored_lens": "Gefärbte Linse", "item.securitycraft.crystal_quartz_item": "Kristallquarz", + "item.securitycraft.dark_oak_security_sea_boat": "Gesichertes Schwarzeichenholz-Seeboot", "item.securitycraft.disguise_module": "Tarnmodul", "item.securitycraft.door_indestructible_iron_item": "Verstärkte Eisentür", "item.securitycraft.harming_module": "Schadensmodul", + "item.securitycraft.jungle_security_sea_boat": "Gesichertes Tropenholz-Seeboot", "item.securitycraft.keycard_holder": "Schlüsselkartenhalter", "item.securitycraft.keycard_lv1": "Level 1 Schlüsselkarte", "item.securitycraft.keycard_lv2": "Level 2 Schlüsselkarte", @@ -935,16 +942,18 @@ "item.securitycraft.keypad_item": "Nummernfeld", "item.securitycraft.lens": "Linse", "item.securitycraft.limited_use_keycard": "Begrenzt nutzbare Schlüsselkarte", + "item.securitycraft.mangrove_security_sea_boat": "Gesichertes Mangrovenholz-Seeboot", + "item.securitycraft.oak_security_sea_boat": "Gesichertes Eichenholz-Seeboot", "item.securitycraft.portable_tune_player": "Tragbarer Melodienspieler", "item.securitycraft.redstone_module": "Redstone-Modul", "item.securitycraft.remote_access_mine": "Kabellose Minensteuerungseinheit", "item.securitycraft.remote_access_sentry": "Kabellose Geschützsteuerungseinheit", "item.securitycraft.sc_manual": "SecurityCraft Anleitung", "item.securitycraft.scanner_door_item": "Scannertür", - "item.securitycraft.security_sea_raft": "Gesichertes Seefloß", "item.securitycraft.sentry": "Geschütz", "item.securitycraft.smart_module": "Schlaues Modul", "item.securitycraft.speed_module": "Schnelligkeits-Modul", + "item.securitycraft.spruce_security_sea_boat": "Gesichertes Fichtenholz-Seeboot", "item.securitycraft.storage_module": "Lagermodul", "item.securitycraft.taser": "Taser", "item.securitycraft.taser_powered": "Geladener Taser", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index d65567b0e5..fc2498fc3b 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -797,6 +797,7 @@ "gui.securitycraft:scManual.reinforced_pressure_plates": "Reinforced Pressure Plates", "gui.securitycraft:scManual.secret_hanging_signs": "Secret Hanging Signs", "gui.securitycraft:scManual.secret_signs": "Secret Signs", + "gui.securitycraft:scManual.security_sea_boats": "Security Sea Boats", "gui.securitycraft:scManual.viewActivatedBlock": "View activated: This block can be activated by hovering over it with your cursor while in block-breaking range.", "gui.securitycraft:sonic_security_system.invert.tooltip_default": "Currently allows access to blocks after the correct tune is played.", "gui.securitycraft:sonic_security_system.invert.tooltip_inverted": "Currently disables access to blocks after the correct tune is played.", @@ -904,7 +905,7 @@ "help.securitycraft.scanner_trapdoor.info": "The Scanner Trapdoor acts like a Retinal Scanner combined with a Reinforced Trapdoor, where the owner looks at the trapdoor to open it.", "help.securitycraft.secret_signs.info": "The Secret Sign works like a vanilla sign, with the slight difference that only the owner of the sign and players on the allowlist can interact with, and view the text on it. Make sure no one can break the block it's placed on!", "help.securitycraft.security_camera.info": "The security camera allows you to view the nearby area from its position. Simply place down a camera, then rightclick it with a camera monitor to bind it for use.", - "help.securitycraft.security_sea_raft.info": "The Security Sea Raft is a chest boat with a Passcode-protected Chest. Anyone who has the correct code can open the chest. Only the owner can break the raft.", + "help.securitycraft.security_sea_boats.info": "The security sea boat is a chest boat with a Passcode-protected Chest. Anyone who has the correct code can open the chest, but only the owner can break the boat. The boat is immune to any damage, even lava. It cannot swim on lava, however.", "help.securitycraft.sentry.info": "The Sentry attacks intruders with an infinite supply of bullets. Alternatively, other projectiles can be used by putting them in a passcode-protected chest or barrel below the Sentry. It has three modes and three target types that can be switched by rightclicking it. The modes are \"Idle\" (it will never attack), \"Camouflage\" (it will only activate once a target is within range) and \"Aggressive\" (it will always be active), with the latter two modes being available with the target types \"Hostile mobs only\", \"Hostile mobs and players\", and \"Players only\". A disguise module will make the Sentry disguise itself with the block inserted in the module, a speed module will increase the Sentry's attack speed, and an allowlist module will prohibit it from shooting listed players. The modules can be inserted or switched out by rightclicking a Sentry, and they can be extracted by using a Universal Block Modifier. To remove the Sentry, sneak-rightclick it.", "help.securitycraft.smart_module.info": "The smart module upgrades the range or functions of a block.", "help.securitycraft.sonic_security_system.info": "The Sonic Security System, inspired by the similarly-named device in the game \"Metroid Prime 2: Echoes\", is able to lock certain SecurityCraft blocks and prevent them from being used or interacted with. Right-clicking a supported block will bind it to the Sonic Security System. Once it is placed down, right-clicking it will open a GUI where you can enable, disable, or reset the Sonic Security System. The blocks can be unlocked by playing a tune nearby, either via the Portable Tune Player, or note blocks. To set such a tune, toggle note recording on in the Sonic Security System's GUI then play the tune that you want using note blocks played nearby. Toggle the recording off once you are finished. Any time that tune is played nearby the Sonic Security System, blocks locked by it will become accessible for a short while.", @@ -920,18 +921,24 @@ "help.securitycraft.username_logger.info": "The username logger will log any player's name within 3 blocks when it is powered by redstone.", "help.securitycraft.whitelist_module.info": "The allowlist module usually removes player(s) from a block's effect or passcode-checking.", "help.securitycraft.wire_cutters.info": "The wire cutters can be used to defuse several types of mines and deactivate a cage trap by rightclicking the block with them. Furthermore, sneak-rightclicking a passcode-protected block removes the passcode protection.", + "item.securitycraft.acacia_security_sea_boat": "Acacia Security Sea Boat", "item.securitycraft.admin_tool": "Admin Tool", + "item.securitycraft.bamboo_security_sea_raft": "Bamboo Security Sea Raft", + "item.securitycraft.birch_security_sea_boat": "Birch Security Sea Boat", "item.securitycraft.blacklist_module": "Denylist Module", "item.securitycraft.briefcase": "Briefcase", "item.securitycraft.bucket_f_lava": "Fake Lava Bucket", "item.securitycraft.bucket_f_water": "Fake Water Bucket", "item.securitycraft.camera_monitor": "Camera Monitor", + "item.securitycraft.cherry_security_sea_boat": "Cherry Security Sea Boat", "item.securitycraft.codebreaker": "Codebreaker", "item.securitycraft.colored_lens": "Colored Lens", "item.securitycraft.crystal_quartz_item": "Crystal Quartz", + "item.securitycraft.dark_oak_security_sea_boat": "Dark Oak Security Sea Boat", "item.securitycraft.disguise_module": "Disguise Module", "item.securitycraft.door_indestructible_iron_item": "Reinforced Iron Door", "item.securitycraft.harming_module": "Harming Module", + "item.securitycraft.jungle_security_sea_boat": "Jungle Security Sea Boat", "item.securitycraft.keycard_holder": "Keycard Holder", "item.securitycraft.keycard_lv1": "Level 1 Keycard", "item.securitycraft.keycard_lv2": "Level 2 Keycard", @@ -943,16 +950,18 @@ "item.securitycraft.keypad_item": "Key Panel", "item.securitycraft.lens": "Lens", "item.securitycraft.limited_use_keycard": "Limited Use Keycard", + "item.securitycraft.mangrove_security_sea_boat": "Mangrove Security Sea Boat", + "item.securitycraft.oak_security_sea_boat": "Oak Security Sea Boat", "item.securitycraft.portable_tune_player": "Portable Tune Player", "item.securitycraft.redstone_module": "Redstone Module", "item.securitycraft.remote_access_mine": "Mine Remote Access Tool", "item.securitycraft.remote_access_sentry": "Sentry Remote Access Tool", "item.securitycraft.sc_manual": "SecurityCraft Manual", "item.securitycraft.scanner_door_item": "Scanner Door", - "item.securitycraft.security_sea_raft": "Security Sea Raft", "item.securitycraft.sentry": "Sentry", "item.securitycraft.smart_module": "Smart Module", "item.securitycraft.speed_module": "Speed Module", + "item.securitycraft.spruce_security_sea_boat": "Spruce Security Sea Boat", "item.securitycraft.storage_module": "Storage Module", "item.securitycraft.taser": "Taser", "item.securitycraft.taser_powered": "Powered Taser", diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/acacia.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/acacia.png new file mode 100644 index 0000000000000000000000000000000000000000..01384e9d76340ee7b62137b77d8e4a39b94e028a GIT binary patch literal 4081 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 Zo3jwV(iRT0`5V&#H;ir>R_G%i{})8!)-3=4 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_raft.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/bamboo.png similarity index 100% rename from src/main/resources/assets/securitycraft/textures/entity/security_sea_raft.png rename to src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/bamboo.png diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/birch.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/birch.png new file mode 100644 index 0000000000000000000000000000000000000000..68bf33e7160d65a588209e9010ebe95283b86bb6 GIT binary patch literal 4053 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 E50ix4)Bpeg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/cherry.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/cherry.png new file mode 100644 index 0000000000000000000000000000000000000000..8f21a2243caef2a0dc0557b3a74d04dd196fc5d1 GIT binary patch literal 4190 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 ZN`uRrU)HK~^WRSZ&{ES=tx~p+_#ZVA^br66 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/dark_oak.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/dark_oak.png new file mode 100644 index 0000000000000000000000000000000000000000..ce1b9f0c057305de2f7590b5e1eee7521ea8b5d1 GIT binary patch literal 4033 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/jungle.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/jungle.png new file mode 100644 index 0000000000000000000000000000000000000000..3c449826279430b5be5204472d1666c11aba64ed GIT binary patch literal 4088 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 N_m1K1N)5Xg{{zc3!HEC> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/mangrove.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/mangrove.png new file mode 100644 index 0000000000000000000000000000000000000000..e2b75823686d2092430e6227491bbb7d53ee7950 GIT binary patch literal 4087 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/oak.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/oak.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c6935fba5a4256735c0564cbadeaefe9652133 GIT binary patch literal 4111 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 F_&+gT+X4Up literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/spruce.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/spruce.png new file mode 100644 index 0000000000000000000000000000000000000000..40a17bb167fb3b43d901120fdcaedb022025870b GIT binary patch literal 4046 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 A(f|Me literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png b/src/main/resources/assets/securitycraft/textures/item/bamboo_security_sea_raft.png similarity index 100% rename from src/main/resources/assets/securitycraft/textures/item/security_sea_raft.png rename to src/main/resources/assets/securitycraft/textures/item/bamboo_security_sea_raft.png diff --git a/src/main/resources/assets/securitycraft/textures/item/birch_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/birch_security_sea_boat.png new file mode 100644 index 0000000000000000000000000000000000000000..913ecdbd822245eb35bf445c27df326c3acbbfb1 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}x0G|-og$oy6yLRo_vuCzW&W#2BbNUlHIyydm`gHwt@0~k$q5~|R-kd*c z)~plTtK5vW*UwJ}>YEYtFBC`#mIV0)|3?E1s%vK^0VO#LJR*x382Ao@Fyrz36)8Z$ zI!_nJ5RKr_^9O|vD{!y`+*VRzd-8w38Mk5kJIA}vc^2zxy?tYG^^)EF@D1sI9XsSI zvb_8mKg{29PPsM1z?`5N Bg*gBK literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/cherry_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/cherry_security_sea_boat.png new file mode 100644 index 0000000000000000000000000000000000000000..3d7cd856efdd67a1262edee9abe4781ea0421025 GIT binary patch literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}B0G|-og$oy6yLRo_vuCzW&SzU2?#`Xw(b4hg)2HW$cHg;kXG47Q$4h5s z&6@Rm*OopDmxn7BKHIz&sMStm;T|9*R1)MD{2v`KJlels2q?>0;1OBOz`%DHgc*8j;I@*I+m!$RZ|WS8*~MNu-?&HY{FKd)r+ELYi9aa! zm0hvw!AeC_mIL)cTH6H{9n)bvBD<)$K#}W0p<_tvuf~~|6ejC9D%{ccILF{n;c<1V z;!$6PWWTAhP1Bt&Om9`$adso`=a3ZD9j>k-mG+g7>X=zwxXKL-D=z?@!rNS%G}x0G|-og$oy6yLRo_vuCzW&L#@n_Uil{9UY%OeM)guxO3-@wm4s7klL(S zv%)QEalo5gdB{pwM9j4wiu1N=j@`{_i*AHf(?Ac=tKaVqLAbZ!E4}vb!I?A^opohg?OL zmp|i&`CHB@w`Lf4Gkt7%p%l}>R1zx?y0G$q7u%5&+$@JaH(lXjXqjGemo4)lQ)HUf zw5I7!7j`cfX}Nu?uTpCh_o2;7U3=!+bnalhG?|NUUzNv1pi3A$UHx3vIVCg!0OekM AMgRZ+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/jungle_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/jungle_security_sea_boat.png new file mode 100644 index 0000000000000000000000000000000000000000..93d5c97c64216d3bd1e4b49a7e6cf33856f9d390 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}x0G|-og$oy6yLRo_vuCzW&SBOXX)Zb)9UY%OecI8Ua_7z+cYXPTQ%h&f znl&ZE)l5OGHpCLBZ|8^G5k-#Vg8nL%B>j&-b^1`UMR(MFqOm#gf6T+;Kg?21UJi}&rMf&7+R*6+-1vr$P}5T zHLYp7(}ms3MOtp(>Z{b+#C>S9QrDjOHk~^dFHPp++gIf=5$F;IPgg&ebxsLQ06Qvy ARR910 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/mangrove_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/mangrove_security_sea_boat.png new file mode 100644 index 0000000000000000000000000000000000000000..e7afdac5f6fd99b69f30467b658d3e563e8caf24 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}x0G|-og$oy6yLRo_vuCzW&cV{MF^UQu9UY%OeX4gdxpU`^yO8j#aHm3==#1Ed5?g8YL2qX7ohwKJ1|lAHw|k;M!Qe1}1p@p%4<6rf<8 zr;B5VMsVo)gF=TDI9LL1D=D!(`M=+c+pztexi*>c$zOlG^$?ksmhV;LV9dZ>} zUjB?9=5INt+?rwF&GfP5g;GohQ%S5q=)%eaUTjBBaI+lx+;oM9p=El>UAD}JOp$3? z)0(C`UD&-`q~-RlzDli4+=n(Rb?upN)47B3(qt~aeN`S4fi7Y2boFyt=akR{0IlbK AV*mgE literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/oak_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/oak_security_sea_boat.png new file mode 100644 index 0000000000000000000000000000000000000000..9c9977b6a70b8587c8abb0d64480e49893b2c174 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}x0G|-og$oy6yLRo_vuCzW&XFFv`B4@f9UY%OecCf4>CT-y?p7*CmsQM~ zHEVv0uceA~bB--g-@$anY#=3A666>B9}O_5uAP|#l;kY%h%9Dc;5!V$jK}j=qyPo$ zJY5_^G=f9V9~3&Qz`+u5TSCe{?2xO- z^73c=Fn`NA<<<-XZ>EneFO*_Bm`Y*=LKjvZ@M1f1f}7>g=cX$>3@y`3?y_Y*WQt7F zn$|Sk>B8>iA}zOX^;K$Z;y$!lscX-Co6a4KmnL)Z?W^*b2y_X9r>mdKI;Vst06;>4 AMF0Q* literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/securitycraft/textures/item/spruce_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/spruce_security_sea_boat.png new file mode 100644 index 0000000000000000000000000000000000000000..24564566a7ae62283fba927e05211d3b5bb11c3d GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}x0G|-og$oy6yLRo_vuCzW&hF-_Ar3kn9UY%OeQHlMxpU`^iH7v-eCt`W zW|f8OYKrqG`{)4m`FWrA15$z|L4Lvi(Ex+$+L=i}NzMX~$YKTtzQZ8Qcszea3Q(}l z)5S4FBRKT@L7~G694rC1m6X_?{NHcJZP@?|(sKJ&U!~S2?n9fEy7tVs>D Date: Thu, 16 May 2024 17:47:23 +0200 Subject: [PATCH 28/55] fix boat drops not being reinforced --- .../securitycraft/entity/SecuritySeaBoat.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 1a86bb405e..a098a227e1 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -141,6 +141,47 @@ public void chestVehicleDestroyed(DamageSource damageSource, Level level, Entity SaltData.removeSalt(getSaltKey()); } + @Override + public ItemEntity spawnAtLocation(ItemStack stack, float offsetY) { + if (stack.getItem() instanceof BlockItem blockItem) { + Block block = blockItem.getBlock(); + Block newBlock = null; + + if (block == Blocks.OAK_PLANKS) + newBlock = SCContent.REINFORCED_OAK_PLANKS.get(); + else if (block == Blocks.SPRUCE_PLANKS) + newBlock = SCContent.REINFORCED_SPRUCE_PLANKS.get(); + else if (block == Blocks.BIRCH_PLANKS) + newBlock = SCContent.REINFORCED_BIRCH_PLANKS.get(); + else if (block == Blocks.JUNGLE_PLANKS) + newBlock = SCContent.REINFORCED_JUNGLE_PLANKS.get(); + else if (block == Blocks.ACACIA_PLANKS) + newBlock = SCContent.REINFORCED_ACACIA_PLANKS.get(); + else if (block == Blocks.DARK_OAK_PLANKS) + newBlock = SCContent.REINFORCED_DARK_OAK_PLANKS.get(); + else if (block == Blocks.MANGROVE_PLANKS) + newBlock = SCContent.REINFORCED_MANGROVE_PLANKS.get(); + else if (block == Blocks.CHERRY_PLANKS) + newBlock = SCContent.REINFORCED_CHERRY_PLANKS.get(); + else if (block == Blocks.BAMBOO_PLANKS) + newBlock = SCContent.REINFORCED_BAMBOO_PLANKS.get(); + + if (newBlock != null) { + ItemStack newStack = new ItemStack(newBlock, stack.getCount()); + + AttachmentUtils.copyStackAttachments(stack, newStack); + newStack.setPopTime(stack.getPopTime()); + + if (stack.getTag() != null) + newStack.setTag(stack.getTag().copy()); + + stack = newStack; + } + } + + return super.spawnAtLocation(stack, offsetY); + } + @Override protected void addAdditionalSaveData(CompoundTag tag) { CompoundTag ownerTag = new CompoundTag(); From c6121b32e1f57f91b3297cd468283de56899a8fa Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Thu, 16 May 2024 18:13:40 +0200 Subject: [PATCH 29/55] make boats not break at all when falling from certain heights --- .../securitycraft/entity/SecuritySeaBoat.java | 41 ------------------- .../securitycraft/mixin/boat/BoatMixin.java | 33 +++++++++++++++ 2 files changed, 33 insertions(+), 41 deletions(-) create mode 100644 src/main/java/net/geforcemods/securitycraft/mixin/boat/BoatMixin.java diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index a098a227e1..1a86bb405e 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -141,47 +141,6 @@ public void chestVehicleDestroyed(DamageSource damageSource, Level level, Entity SaltData.removeSalt(getSaltKey()); } - @Override - public ItemEntity spawnAtLocation(ItemStack stack, float offsetY) { - if (stack.getItem() instanceof BlockItem blockItem) { - Block block = blockItem.getBlock(); - Block newBlock = null; - - if (block == Blocks.OAK_PLANKS) - newBlock = SCContent.REINFORCED_OAK_PLANKS.get(); - else if (block == Blocks.SPRUCE_PLANKS) - newBlock = SCContent.REINFORCED_SPRUCE_PLANKS.get(); - else if (block == Blocks.BIRCH_PLANKS) - newBlock = SCContent.REINFORCED_BIRCH_PLANKS.get(); - else if (block == Blocks.JUNGLE_PLANKS) - newBlock = SCContent.REINFORCED_JUNGLE_PLANKS.get(); - else if (block == Blocks.ACACIA_PLANKS) - newBlock = SCContent.REINFORCED_ACACIA_PLANKS.get(); - else if (block == Blocks.DARK_OAK_PLANKS) - newBlock = SCContent.REINFORCED_DARK_OAK_PLANKS.get(); - else if (block == Blocks.MANGROVE_PLANKS) - newBlock = SCContent.REINFORCED_MANGROVE_PLANKS.get(); - else if (block == Blocks.CHERRY_PLANKS) - newBlock = SCContent.REINFORCED_CHERRY_PLANKS.get(); - else if (block == Blocks.BAMBOO_PLANKS) - newBlock = SCContent.REINFORCED_BAMBOO_PLANKS.get(); - - if (newBlock != null) { - ItemStack newStack = new ItemStack(newBlock, stack.getCount()); - - AttachmentUtils.copyStackAttachments(stack, newStack); - newStack.setPopTime(stack.getPopTime()); - - if (stack.getTag() != null) - newStack.setTag(stack.getTag().copy()); - - stack = newStack; - } - } - - return super.spawnAtLocation(stack, offsetY); - } - @Override protected void addAdditionalSaveData(CompoundTag tag) { CompoundTag ownerTag = new CompoundTag(); diff --git a/src/main/java/net/geforcemods/securitycraft/mixin/boat/BoatMixin.java b/src/main/java/net/geforcemods/securitycraft/mixin/boat/BoatMixin.java new file mode 100644 index 0000000000..fbbb816be2 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/mixin/boat/BoatMixin.java @@ -0,0 +1,33 @@ +package net.geforcemods.securitycraft.mixin.boat; + +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; + +import net.geforcemods.securitycraft.SCContent; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +/** + * Fixes MC-119369 for SecurityCraft's security sea boats, as they + * should not be destroyable by anyone other than the owner. + */ +@Mixin(Boat.class) +public abstract class BoatMixin extends Entity { + protected BoatMixin(EntityType type, Level level) { + super(type, level); + } + + @Inject(method = "checkFallDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/vehicle/Boat;causeFallDamage(FFLnet/minecraft/world/damagesource/DamageSource;)Z"), cancellable = true) + private void securitycraft$fixMC199369(double y, boolean onGround, BlockState state, BlockPos pos, CallbackInfo ci) { + if (getType() == SCContent.SECURITY_SEA_BOAT_ENTITY.get()) { + resetFallDistance(); + ci.cancel(); + } + } +} From 75d2858e2169c14d2bcdf7a378b761b1827f734d Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Fri, 17 May 2024 14:02:51 +0200 Subject: [PATCH 30/55] start work on module and option support --- .../securitycraft/ClientHandler.java | 1 + .../geforcemods/securitycraft/SCContent.java | 1 + .../securitycraft/entity/SecuritySeaBoat.java | 179 +++++++++++++++--- .../inventory/CustomizeBlockMenu.java | 5 +- .../items/UniversalBlockModifierItem.java | 3 +- .../network/server/ToggleModule.java | 40 +++- .../network/server/ToggleOption.java | 46 ++++- .../screen/CustomizeBlockScreen.java | 12 +- .../assets/securitycraft/lang/de_at.json | 4 +- .../assets/securitycraft/lang/de_ch.json | 4 +- .../assets/securitycraft/lang/de_de.json | 4 +- .../assets/securitycraft/lang/en_us.json | 4 +- src/main/resources/securitycraft.mixins.json | 1 + 13 files changed, 250 insertions(+), 54 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index 1940f4abb8..4e3dee486b 100644 --- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java @@ -387,6 +387,7 @@ public static void registerMenuScreens(RegisterMenuScreensEvent event) { event.register(SCContent.BLOCK_REINFORCER_MENU.get(), BlockReinforcerScreen::new); event.register(SCContent.BRIEFCASE_INVENTORY_MENU.get(), ItemInventoryScreen.Briefcase::new); event.register(SCContent.CUSTOMIZE_BLOCK_MENU.get(), CustomizeBlockScreen::new); + event.register(SCContent.CUSTOMIZE_ENTITY_MENU.get(), CustomizeBlockScreen::new); event.register(SCContent.DISGUISE_MODULE_MENU.get(), DisguiseModuleScreen::new); event.register(SCContent.INVENTORY_SCANNER_MENU.get(), InventoryScannerScreen::new); event.register(SCContent.KEYPAD_FURNACE_MENU.get(), KeypadFurnaceScreen::new); diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index 3afeaed90f..a17bd4088c 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -2951,6 +2951,7 @@ else if (state.is(BOUNCING_BETTY)) public static final DeferredHolder, MenuType> BLOCK_REINFORCER_MENU = MENU_TYPES.register("block_reinforcer", () -> IMenuTypeExtension.create((windowId, inv, data) -> new BlockReinforcerMenu(windowId, inv, data.readBoolean()))); public static final DeferredHolder, MenuType> BRIEFCASE_INVENTORY_MENU = MENU_TYPES.register("briefcase_inventory", () -> IMenuTypeExtension.create((windowId, inv, data) -> new BriefcaseMenu(windowId, inv, ItemContainer.briefcase(PlayerUtils.getItemStackFromAnyHand(inv.player, SCContent.BRIEFCASE.get()))))); public static final DeferredHolder, MenuType> CUSTOMIZE_BLOCK_MENU = MENU_TYPES.register("customize_block", () -> IMenuTypeExtension.create((windowId, inv, data) -> new CustomizeBlockMenu(windowId, inv.player.level(), data.readBlockPos(), inv))); + public static final DeferredHolder, MenuType> CUSTOMIZE_ENTITY_MENU = MENU_TYPES.register("customize_entity", () -> IMenuTypeExtension.create((windowId, inv, data) -> new CustomizeBlockMenu(windowId, inv.player.level(), data.readBlockPos(), data.readVarInt(), inv))); public static final DeferredHolder, MenuType> DISGUISE_MODULE_MENU = MENU_TYPES.register("disguise_module", () -> IMenuTypeExtension.create((windowId, inv, data) -> new DisguiseModuleMenu(windowId, inv, new ModuleItemContainer(PlayerUtils.getItemStackFromAnyHand(inv.player, SCContent.DISGUISE_MODULE.get()))))); public static final DeferredHolder, MenuType> INVENTORY_SCANNER_MENU = MENU_TYPES.register("inventory_scanner", () -> IMenuTypeExtension.create((windowId, inv, data) -> new InventoryScannerMenu(windowId, inv.player.level(), data.readBlockPos(), inv))); public static final DeferredHolder, MenuType> KEYPAD_FURNACE_MENU = MENU_TYPES.register("keypad_furnace", () -> IMenuTypeExtension.create((windowId, inv, data) -> new KeypadFurnaceMenu(windowId, inv.player.level(), data.readBlockPos(), inv))); diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 1a86bb405e..2a8cc52714 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -1,12 +1,23 @@ package net.geforcemods.securitycraft.entity; +import java.util.EnumMap; +import java.util.Map; import java.util.UUID; import net.geforcemods.securitycraft.SCContent; import net.geforcemods.securitycraft.SCTags; +import net.geforcemods.securitycraft.api.ICustomizable; +import net.geforcemods.securitycraft.api.IModuleInventory; import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.api.IPasscodeProtected; +import net.geforcemods.securitycraft.api.Option; +import net.geforcemods.securitycraft.api.Option.BooleanOption; +import net.geforcemods.securitycraft.api.Option.SendAllowlistMessageOption; +import net.geforcemods.securitycraft.api.Option.SendDenylistMessageOption; +import net.geforcemods.securitycraft.api.Option.SmartModuleCooldownOption; import net.geforcemods.securitycraft.api.Owner; +import net.geforcemods.securitycraft.inventory.CustomizeBlockMenu; +import net.geforcemods.securitycraft.misc.ModuleType; import net.geforcemods.securitycraft.misc.SaltData; import net.geforcemods.securitycraft.network.client.OpenScreen; import net.geforcemods.securitycraft.network.client.OpenScreen.DataType; @@ -15,28 +26,40 @@ import net.geforcemods.securitycraft.util.Utils; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Containers; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.MenuProvider; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.entity.vehicle.ChestBoat; +import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.PacketDistributor; -public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodeProtected { +public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodeProtected, IModuleInventory, ICustomizable { private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaBoat.class, Owner.getSerializer()); private byte[] passcode; private UUID saltKey; + private NonNullList modules = NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY); + private BooleanOption sendAllowlistMessage = new SendAllowlistMessageOption(false); + private BooleanOption sendDenylistMessage = new SendDenylistMessageOption(true); + private SmartModuleCooldownOption smartModuleCooldown = new SmartModuleCooldownOption(); + private long cooldownEnd = 0; + private Map moduleStates = new EnumMap<>(ModuleType.class); public SecuritySeaBoat(EntityType type, Level level) { super(SCContent.SECURITY_SEA_BOAT_ENTITY.get(), level); @@ -78,16 +101,38 @@ else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) { return InteractionResult.sidedSuccess(level.isClientSide); } - else if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) { + else if (isOwnedBy(player)) { if (!level.isClientSide) { - String newOwner = stack.getHoverName().getString(); - - setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); + if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get())) { + String newOwner = stack.getHoverName().getString(); + + setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); + } + else if (stack.is(SCContent.UNIVERSAL_BLOCK_MODIFIER.get())) { + BlockPos pos = blockPosition(); + + player.openMenu(new MenuProvider() { + @Override + public AbstractContainerMenu createMenu(int windowId, Inventory inv, Player player) { + return new CustomizeBlockMenu(windowId, level, pos, SecuritySeaBoat.super.getId(), inv); + } + + @Override + public Component getDisplayName() { + return SecuritySeaBoat.super.getDisplayName(); + } + }, data -> { + data.writeBlockPos(pos); + data.writeVarInt(SecuritySeaBoat.super.getId()); + }); + } } return InteractionResult.sidedSuccess(level.isClientSide); } + else + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_BLOCK_MODIFIER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); } return super.interact(player, hand); @@ -98,8 +143,20 @@ public InteractionResult interactWithContainerVehicle(Player player) { Level level = level(); BlockPos pos = blockPosition(); - if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player)) - openPasscodeGUI(level, pos, player); + if (!level.isClientSide && verifyPasscodeSet(level, pos, this, player)) { + if (isDenied(player)) { + if (sendsDenylistMessage()) + PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:module.onDenylist"), ChatFormatting.RED); + } + else if (isAllowed(player)) { + if (sendsAllowlistMessage()) + PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:module.onAllowlist"), ChatFormatting.GREEN); + + activate(player); + } + else + openPasscodeGUI(level, pos, player); + } return !level.isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } @@ -141,11 +198,40 @@ public void chestVehicleDestroyed(DamageSource damageSource, Level level, Entity SaltData.removeSalt(getSaltKey()); } + @Override + public Item getDropItem() { + return (switch (getVariant()) { + case SPRUCE -> SCContent.SPRUCE_SECURITY_SEA_BOAT; + case BIRCH -> SCContent.BIRCH_SECURITY_SEA_BOAT; + case JUNGLE -> SCContent.JUNGLE_SECURITY_SEA_BOAT; + case ACACIA -> SCContent.ACACIA_SECURITY_SEA_BOAT; + case DARK_OAK -> SCContent.DARK_OAK_SECURITY_SEA_BOAT; + case MANGROVE -> SCContent.MANGROVE_SECURITY_SEA_BOAT; + case CHERRY -> SCContent.CHERRY_SECURITY_SEA_BOAT; + case BAMBOO -> SCContent.BAMBOO_SECURITY_SEA_RAFT; + default -> SCContent.OAK_SECURITY_SEA_BOAT; + }).get(); + } + + @Override + public void remove(RemovalReason reason) { + if (!level().isClientSide && reason.shouldDestroy()) + Containers.dropContents(level(), blockPosition(), modules); + + super.remove(reason); + } + @Override protected void addAdditionalSaveData(CompoundTag tag) { CompoundTag ownerTag = new CompoundTag(); + long cooldownLeft; super.addAdditionalSaveData(tag); + writeModuleInventory(tag); + writeModuleStates(tag); + writeOptions(tag); + cooldownLeft = getCooldownEnd() - System.currentTimeMillis(); + tag.putLong("cooldownLeft", cooldownLeft <= 0 ? -1 : cooldownLeft); getOwner().save(ownerTag, needsValidation()); tag.put("owner", ownerTag); @@ -159,6 +245,10 @@ protected void addAdditionalSaveData(CompoundTag tag) { @Override protected void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); + modules = readModuleInventory(tag); + moduleStates = readModuleStates(tag); + readOptions(tag); + cooldownEnd = System.currentTimeMillis() + tag.getLong("cooldownLeft"); entityData.set(OWNER, Owner.fromCompound(tag.getCompound("owner"))); loadSaltKey(tag); loadPasscode(tag); @@ -208,30 +298,73 @@ public void setSaltKey(UUID saltKey) { } @Override - public void startCooldown() {} + public void startCooldown() { + if (!isOnCooldown()) + cooldownEnd = System.currentTimeMillis() + smartModuleCooldown.get() * 50; + } @Override public boolean isOnCooldown() { - return false; + return System.currentTimeMillis() < getCooldownEnd(); } @Override public long getCooldownEnd() { - return 0; + return cooldownEnd; } @Override - public Item getDropItem() { - return (switch (getVariant()) { - case SPRUCE -> SCContent.SPRUCE_SECURITY_SEA_BOAT; - case BIRCH -> SCContent.BIRCH_SECURITY_SEA_BOAT; - case JUNGLE -> SCContent.JUNGLE_SECURITY_SEA_BOAT; - case ACACIA -> SCContent.ACACIA_SECURITY_SEA_BOAT; - case DARK_OAK -> SCContent.DARK_OAK_SECURITY_SEA_BOAT; - case MANGROVE -> SCContent.MANGROVE_SECURITY_SEA_BOAT; - case CHERRY -> SCContent.CHERRY_SECURITY_SEA_BOAT; - case BAMBOO -> SCContent.BAMBOO_SECURITY_SEA_RAFT; - default -> SCContent.OAK_SECURITY_SEA_BOAT; - }).get(); + public Option[] customOptions() { + return new Option[] { + sendAllowlistMessage, sendDenylistMessage, smartModuleCooldown + }; + } + + @Override + public NonNullList getInventory() { + return modules; + } + + @Override + public ModuleType[] acceptedModules() { + return new ModuleType[] { + ModuleType.ALLOWLIST, ModuleType.DENYLIST, ModuleType.SMART, ModuleType.HARMING + }; + } + + @Override + public boolean isModuleEnabled(ModuleType module) { + return hasModule(module) && moduleStates.get(module) == Boolean.TRUE; //prevent NPE + } + + @Override + public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { + moduleStates.put(module, shouldBeEnabled); + } + + @Override + public Level myLevel() { + return level(); + } + + @Override + public BlockPos myPos() { + return blockPosition(); + } + + public boolean sendsAllowlistMessage() { + return sendAllowlistMessage.get(); + } + + public void setSendsAllowlistMessage(boolean value) { + sendAllowlistMessage.setValue(value); + } + + public boolean sendsDenylistMessage() { + return sendDenylistMessage.get(); + } + + public void setSendsDenylistMessage(boolean value) { + sendDenylistMessage.setValue(value); } } diff --git a/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java b/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java index 796f9b2db7..f8146ef2e6 100644 --- a/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java +++ b/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java @@ -20,19 +20,22 @@ public class CustomizeBlockMenu extends AbstractContainerMenu { public final IModuleInventory moduleInv; private ContainerLevelAccess worldPosCallable; private int maxSlots; + public final int entityId; public CustomizeBlockMenu(int windowId, Level level, BlockPos pos, Inventory inventory) { super(SCContent.CUSTOMIZE_BLOCK_MENU.get(), windowId); moduleInv = (IModuleInventory) level.getBlockEntity(pos); worldPosCallable = ContainerLevelAccess.create(level, pos); addSlots(inventory); + entityId = -1; } public CustomizeBlockMenu(int windowId, Level level, BlockPos pos, int entityId, Inventory inventory) { - super(SCContent.CUSTOMIZE_BLOCK_MENU.get(), windowId); + super(SCContent.CUSTOMIZE_ENTITY_MENU.get(), windowId); moduleInv = (IModuleInventory) level.getEntity(entityId); worldPosCallable = ContainerLevelAccess.create(level, pos); addSlots(inventory); + this.entityId = entityId; } public void addSlots(Inventory inventory) { diff --git a/src/main/java/net/geforcemods/securitycraft/items/UniversalBlockModifierItem.java b/src/main/java/net/geforcemods/securitycraft/items/UniversalBlockModifierItem.java index 433a7b2cc3..0c36c6278c 100644 --- a/src/main/java/net/geforcemods/securitycraft/items/UniversalBlockModifierItem.java +++ b/src/main/java/net/geforcemods/securitycraft/items/UniversalBlockModifierItem.java @@ -1,6 +1,5 @@ package net.geforcemods.securitycraft.items; -import net.geforcemods.securitycraft.SCContent; import net.geforcemods.securitycraft.api.IModuleInventory; import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.blockentities.DisplayCaseBlockEntity; @@ -41,7 +40,7 @@ public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext ctx) { else if (be instanceof IModuleInventory) { if (be instanceof IOwnable ownable && !ownable.isOwnedBy(player)) { if (!(be.getBlockState().getBlock() instanceof DisguisableBlock db) || (((BlockItem) db.getDisguisedStack(level, pos).getItem()).getBlock() instanceof DisguisableBlock)) - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_BLOCK_MODIFIER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(ownable.getOwner())), ChatFormatting.RED); + PlayerUtils.sendMessageToPlayer(player, Utils.localize(getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(ownable.getOwner())), ChatFormatting.RED); return InteractionResult.FAIL; } diff --git a/src/main/java/net/geforcemods/securitycraft/network/server/ToggleModule.java b/src/main/java/net/geforcemods/securitycraft/network/server/ToggleModule.java index ffe1ec92f0..8c4b2386bd 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/server/ToggleModule.java +++ b/src/main/java/net/geforcemods/securitycraft/network/server/ToggleModule.java @@ -1,7 +1,6 @@ package net.geforcemods.securitycraft.network.server; import net.geforcemods.securitycraft.SecurityCraft; -import net.geforcemods.securitycraft.api.CustomizableBlockEntity; import net.geforcemods.securitycraft.api.ILinkedAction; import net.geforcemods.securitycraft.api.IModuleInventory; import net.geforcemods.securitycraft.api.IOwnable; @@ -14,6 +13,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.handling.PlayPayloadContext; @@ -21,6 +21,7 @@ public class ToggleModule implements CustomPacketPayload { public static final ResourceLocation ID = new ResourceLocation(SecurityCraft.MODID, "toggle_module"); private BlockPos pos; private ModuleType moduleType; + private int entityId; public ToggleModule() {} @@ -29,14 +30,31 @@ public ToggleModule(BlockPos pos, ModuleType moduleType) { this.moduleType = moduleType; } + public ToggleModule(int entityId, ModuleType moduleType) { + this.entityId = entityId; + this.moduleType = moduleType; + } + public ToggleModule(FriendlyByteBuf buf) { - pos = BlockPos.of(buf.readLong()); + if (buf.readBoolean()) + pos = buf.readBlockPos(); + else + entityId = buf.readVarInt(); + moduleType = buf.readEnum(ModuleType.class); } @Override public void write(FriendlyByteBuf buf) { - buf.writeLong(pos.asLong()); + boolean hasPos = pos != null; + + buf.writeBoolean(hasPos); + + if (hasPos) + buf.writeBlockPos(pos); + else + buf.writeVarInt(entityId); + buf.writeEnum(moduleType); } @@ -47,26 +65,32 @@ public ResourceLocation id() { public void handle(PlayPayloadContext ctx) { Player player = ctx.player().orElseThrow(); - BlockEntity be = player.level().getBlockEntity(pos); + Level level = player.level(); + IModuleInventory moduleInv = null; + + if (pos != null && level.getBlockEntity(pos) instanceof IModuleInventory be) + moduleInv = be; + else if (pos == null && level.getEntity(entityId) instanceof IModuleInventory entity) + moduleInv = entity; - if (be instanceof IModuleInventory moduleInv && (!(be instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { + if (moduleInv != null && (!(moduleInv instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { if (moduleInv.isModuleEnabled(moduleType)) { moduleInv.removeModule(moduleType, true); - if (be instanceof LinkableBlockEntity linkable) + if (moduleInv instanceof LinkableBlockEntity linkable) linkable.propagate(new ILinkedAction.ModuleRemoved(moduleType, true), linkable); } else { moduleInv.insertModule(moduleInv.getModule(moduleType), true); - if (be instanceof LinkableBlockEntity linkable) { + if (moduleInv instanceof LinkableBlockEntity linkable) { ItemStack stack = moduleInv.getModule(moduleType); linkable.propagate(new ILinkedAction.ModuleInserted(stack, (ModuleItem) stack.getItem(), true), linkable); } } - if (be instanceof CustomizableBlockEntity) + if (moduleInv instanceof BlockEntity be) player.level().sendBlockUpdated(pos, be.getBlockState(), be.getBlockState(), 3); } } diff --git a/src/main/java/net/geforcemods/securitycraft/network/server/ToggleOption.java b/src/main/java/net/geforcemods/securitycraft/network/server/ToggleOption.java index 65d183f975..7e4fa9ccd9 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/server/ToggleOption.java +++ b/src/main/java/net/geforcemods/securitycraft/network/server/ToggleOption.java @@ -8,30 +8,48 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.handling.PlayPayloadContext; public class ToggleOption implements CustomPacketPayload { public static final ResourceLocation ID = new ResourceLocation(SecurityCraft.MODID, "toggle_option"); private BlockPos pos; - private int optionId; + private int optionId, entityId; public ToggleOption() {} - public ToggleOption(BlockPos pos, int opionId) { + public ToggleOption(BlockPos pos, int optionId) { this.pos = pos; - this.optionId = opionId; + this.optionId = optionId; + } + + public ToggleOption(int entityId, int optionId) { + this.entityId = entityId; + this.optionId = optionId; } public ToggleOption(FriendlyByteBuf buf) { - pos = buf.readBlockPos(); - optionId = buf.readInt(); + if (buf.readBoolean()) + pos = buf.readBlockPos(); + else + entityId = buf.readVarInt(); + + optionId = buf.readVarInt(); } @Override public void write(FriendlyByteBuf buf) { - buf.writeBlockPos(pos); - buf.writeInt(optionId); + boolean hasPos = pos != null; + + buf.writeBoolean(hasPos); + + if (hasPos) + buf.writeBlockPos(pos); + else + buf.writeVarInt(entityId); + + buf.writeVarInt(optionId); } @Override @@ -41,12 +59,20 @@ public ResourceLocation id() { public void handle(PlayPayloadContext ctx) { Player player = ctx.player().orElseThrow(); - BlockEntity be = player.level().getBlockEntity(pos); + Level level = player.level(); + ICustomizable customizable = null; - if (be instanceof ICustomizable customizable && (!(be instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { + if (pos != null && level.getBlockEntity(pos) instanceof ICustomizable be) + customizable = be; + else if (pos == null && level.getEntity(entityId) instanceof ICustomizable entity) + customizable = entity; + + if (customizable != null && (!(customizable instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { customizable.customOptions()[optionId].toggle(); customizable.onOptionChanged(customizable.customOptions()[optionId]); - player.level().sendBlockUpdated(pos, be.getBlockState(), be.getBlockState(), 3); + + if (customizable instanceof BlockEntity be) + level.sendBlockUpdated(pos, be.getBlockState(), be.getBlockState(), 3); } } } diff --git a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java index d83cb6a962..1b66d2d08d 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java @@ -199,7 +199,10 @@ private void moduleButtonClicked(Button button) { moduleInv.insertModule(moduleInv.getModule(moduleType), true); } - PacketDistributor.SERVER.noArg().send(new ToggleModule(moduleInv.myPos(), moduleType)); + if (menu.entityId == -1) + PacketDistributor.SERVER.noArg().send(new ToggleModule(moduleInv.myPos(), moduleType)); + else + PacketDistributor.SERVER.noArg().send(new ToggleModule(menu.entityId, moduleType)); } private void optionButtonClicked(Button button) { @@ -213,7 +216,12 @@ private void optionButtonClicked(Button button) { button.setFGColor(tempOption.toString().equals(tempOption.getDefaultValue().toString()) ? 16777120 : 14737632); button.setMessage(getOptionButtonTitle(tempOption)); optionButtons[i].setTooltip(Tooltip.create(getOptionDescription(i))); - PacketDistributor.SERVER.noArg().send(new ToggleOption(moduleInv.myPos(), i)); + + if (menu.entityId == -1) + PacketDistributor.SERVER.noArg().send(new ToggleOption(moduleInv.myPos(), i)); + else + PacketDistributor.SERVER.noArg().send(new ToggleOption(menu.entityId, i)); + return; } } diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index 20ebc09f4e..4bb79e7a77 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -1229,9 +1229,9 @@ "option.generic.secret_sign.isFrontSecret": "Geheime Vorderseite: %s", "option.generic.secret_sign.isFrontSecret.description": "Soll der Text auf der Vorderseite des geheimen Schildes nur für den Besitzer und zugelassenen Spieler sichtbar sein?", "option.generic.sendAllowlistMessage": "Zulassungslisten-Nachricht: %s", - "option.generic.sendAllowlistMessage.description": "Sendet dieser Block eine Nachricht, wenn er von einem Spieler auf der Zulassungsliste aktiviert wird?", + "option.generic.sendAllowlistMessage.description": "Wird eine Nachricht gesendet, wenn ein Spieler auf der Zulassungsliste hiermit interagiert?", "option.generic.sendDenylistMessage": "Sperrlisten-Nachricht: %s", - "option.generic.sendDenylistMessage.description": "Sendet dieser Block eine Nachricht, wenn er von einem Spieler auf der Sperrliste aktiviert wird?", + "option.generic.sendDenylistMessage.description": "Wird eine Nachricht gesendet, wenn ein Spieler auf der Sperrliste hiermit interagiert?", "option.generic.signalLength": "Signallänge: %s", "option.generic.signalLength.description": "Wie lange soll das Redstonesignal bei erfolgreicher Aktivierung sein? (In Ticks, 20 Ticks = 1 Sekunde. Setze auf 0, um das Signal umzuschalten)", "option.generic.smartModuleCooldown": "Abklingzeit: %s", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index 20ebc09f4e..4bb79e7a77 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -1229,9 +1229,9 @@ "option.generic.secret_sign.isFrontSecret": "Geheime Vorderseite: %s", "option.generic.secret_sign.isFrontSecret.description": "Soll der Text auf der Vorderseite des geheimen Schildes nur für den Besitzer und zugelassenen Spieler sichtbar sein?", "option.generic.sendAllowlistMessage": "Zulassungslisten-Nachricht: %s", - "option.generic.sendAllowlistMessage.description": "Sendet dieser Block eine Nachricht, wenn er von einem Spieler auf der Zulassungsliste aktiviert wird?", + "option.generic.sendAllowlistMessage.description": "Wird eine Nachricht gesendet, wenn ein Spieler auf der Zulassungsliste hiermit interagiert?", "option.generic.sendDenylistMessage": "Sperrlisten-Nachricht: %s", - "option.generic.sendDenylistMessage.description": "Sendet dieser Block eine Nachricht, wenn er von einem Spieler auf der Sperrliste aktiviert wird?", + "option.generic.sendDenylistMessage.description": "Wird eine Nachricht gesendet, wenn ein Spieler auf der Sperrliste hiermit interagiert?", "option.generic.signalLength": "Signallänge: %s", "option.generic.signalLength.description": "Wie lange soll das Redstonesignal bei erfolgreicher Aktivierung sein? (In Ticks, 20 Ticks = 1 Sekunde. Setze auf 0, um das Signal umzuschalten)", "option.generic.smartModuleCooldown": "Abklingzeit: %s", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index 20ebc09f4e..4bb79e7a77 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -1229,9 +1229,9 @@ "option.generic.secret_sign.isFrontSecret": "Geheime Vorderseite: %s", "option.generic.secret_sign.isFrontSecret.description": "Soll der Text auf der Vorderseite des geheimen Schildes nur für den Besitzer und zugelassenen Spieler sichtbar sein?", "option.generic.sendAllowlistMessage": "Zulassungslisten-Nachricht: %s", - "option.generic.sendAllowlistMessage.description": "Sendet dieser Block eine Nachricht, wenn er von einem Spieler auf der Zulassungsliste aktiviert wird?", + "option.generic.sendAllowlistMessage.description": "Wird eine Nachricht gesendet, wenn ein Spieler auf der Zulassungsliste hiermit interagiert?", "option.generic.sendDenylistMessage": "Sperrlisten-Nachricht: %s", - "option.generic.sendDenylistMessage.description": "Sendet dieser Block eine Nachricht, wenn er von einem Spieler auf der Sperrliste aktiviert wird?", + "option.generic.sendDenylistMessage.description": "Wird eine Nachricht gesendet, wenn ein Spieler auf der Sperrliste hiermit interagiert?", "option.generic.signalLength": "Signallänge: %s", "option.generic.signalLength.description": "Wie lange soll das Redstonesignal bei erfolgreicher Aktivierung sein? (In Ticks, 20 Ticks = 1 Sekunde. Setze auf 0, um das Signal umzuschalten)", "option.generic.smartModuleCooldown": "Abklingzeit: %s", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index fc2498fc3b..a22661b617 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -1238,9 +1238,9 @@ "option.generic.secret_sign.isFrontSecret": "Secret Front: %s", "option.generic.secret_sign.isFrontSecret.description": "Should the text on the front of the secret sign only be visible to the owner and players on the allowlist?", "option.generic.sendAllowlistMessage": "Allowlist Message: %s", - "option.generic.sendAllowlistMessage.description": "Does this block send a message upon activation when the player is added to the allowlist?", + "option.generic.sendAllowlistMessage.description": "Does a message get sent upon activation when the player is added to the allowlist?", "option.generic.sendDenylistMessage": "Denylist Message: %s", - "option.generic.sendDenylistMessage.description": "Does this block send a message upon activation when the player is added to the denylist?", + "option.generic.sendDenylistMessage.description": "Does a message get sent upon activation when the player is added to the denylist?", "option.generic.signalLength": "Signal length: %s", "option.generic.signalLength.description": "How long should the redstone signal upon successful activation be? (In ticks, 20 ticks = 1 second. Set to 0 to toggle the signal)", "option.generic.smartModuleCooldown": "Cooldown: %s", diff --git a/src/main/resources/securitycraft.mixins.json b/src/main/resources/securitycraft.mixins.json index 5f4084094b..def62128f0 100644 --- a/src/main/resources/securitycraft.mixins.json +++ b/src/main/resources/securitycraft.mixins.json @@ -13,6 +13,7 @@ "taser.ItemInHandRendererMixin" ], "mixins": [ + "boat.BoatMixin", "camera.ChunkMapMixin", "camera.PlayerListMixin", "camera.ServerPlayerMixin", From 0bd537ac439398cb9124c39a02d369bf0270576a Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Fri, 17 May 2024 14:10:06 +0200 Subject: [PATCH 31/55] fix some interactions not working from outside the boat --- .../securitycraft/entity/SecuritySeaBoat.java | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 2a8cc52714..34f079e31a 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -101,38 +101,38 @@ else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) { return InteractionResult.sidedSuccess(level.isClientSide); } - else if (isOwnedBy(player)) { + else if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) { if (!level.isClientSide) { - if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get())) { - String newOwner = stack.getHoverName().getString(); - - setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); - } - else if (stack.is(SCContent.UNIVERSAL_BLOCK_MODIFIER.get())) { - BlockPos pos = blockPosition(); - - player.openMenu(new MenuProvider() { - @Override - public AbstractContainerMenu createMenu(int windowId, Inventory inv, Player player) { - return new CustomizeBlockMenu(windowId, level, pos, SecuritySeaBoat.super.getId(), inv); - } - - @Override - public Component getDisplayName() { - return SecuritySeaBoat.super.getDisplayName(); - } - }, data -> { - data.writeBlockPos(pos); - data.writeVarInt(SecuritySeaBoat.super.getId()); - }); - } + String newOwner = stack.getHoverName().getString(); + + setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); + } + + return InteractionResult.sidedSuccess(level.isClientSide); + } + else if (stack.is(SCContent.UNIVERSAL_BLOCK_MODIFIER.get()) && isOwnedBy(player)) { + if (!level.isClientSide) { + BlockPos pos = blockPosition(); + + player.openMenu(new MenuProvider() { + @Override + public AbstractContainerMenu createMenu(int windowId, Inventory inv, Player player) { + return new CustomizeBlockMenu(windowId, level, pos, SecuritySeaBoat.super.getId(), inv); + } + + @Override + public Component getDisplayName() { + return SecuritySeaBoat.super.getDisplayName(); + } + }, data -> { + data.writeBlockPos(pos); + data.writeVarInt(SecuritySeaBoat.super.getId()); + }); } return InteractionResult.sidedSuccess(level.isClientSide); } - else - PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_BLOCK_MODIFIER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); } return super.interact(player, hand); From fd6ca1d97823a65d64a753680e6c9b428c50bfcf Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 11:31:06 +0200 Subject: [PATCH 32/55] fix options not synchronizing --- Changelog.md | 2 + .../securitycraft/api/ICustomizable.java | 22 +-- .../geforcemods/securitycraft/api/Option.java | 156 +++++++++++++++++- .../blocks/mines/ExplosiveBlock.java | 4 + .../securitycraft/entity/SecuritySeaBoat.java | 41 ++++- .../network/server/UpdateSliderValue.java | 50 +++++- .../screen/CustomizeBlockScreen.java | 30 ++-- 7 files changed, 254 insertions(+), 51 deletions(-) diff --git a/Changelog.md b/Changelog.md index a22cc1b5f3..0682de14ff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,8 @@ - API: IModuleInventory is no longer hardcoded to just block entities - API: New method ICodebreakable#handleCodebreaking to define behavior when a codebreaker is used to break the code - API: The BlockState parameters in ICodebreakable's methods have been removed +- API: New Option "EntityDataWrappedOption" that connects an EntityDataAccessor with an Option, and corresponding converter method "wrapForEntityData" +- API: New method Option#getValueText for getting a textual representation of the option's value - Fix: Trying to place a Panic Button on top of powdered snow crashes the game - Fix: Occasional crash when opening the inventory in creative mode in certain situations - Fix: Reinforced fence gates don't properly retain their owner when reloading the world diff --git a/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java b/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java index 0323dbc613..e8c1921d69 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java +++ b/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java @@ -4,34 +4,28 @@ import net.minecraft.world.level.block.entity.BlockEntity; /** - * Let your TileEntity implement this to be able to add options to it + * Implement this to be able to add options to the object * * @author bl4ckscor3 */ public interface ICustomizable { /** - * @return The block entity this is for - */ - public default BlockEntity getTheBlockEntity() { - return (BlockEntity) this; - } - - /** - * @return An array of what custom {@link Option}s this TileEntity has. + * @return An array of what custom {@link Option}s this bject has. */ public Option[] customOptions(); /** - * Called whenever an {@link Option} in this TileEntity changes its value + * Called whenever an {@link Option} in this object changes its value * * @param option The changed Option */ public default void onOptionChanged(Option option) { - getTheBlockEntity().setChanged(); + if (this instanceof BlockEntity be) + be.setChanged(); } /** - * Call this from your read method. Used for reading the options from a tag. Use in conjunction with writeOptions. + * Used for reading the options from a tag. Use in conjunction with writeOptions. * * @param tag The tag to read the options from */ @@ -46,10 +40,10 @@ public default void readOptions(CompoundTag tag) { } /** - * Call this from your write method. Used for writing the options to a tag. Use in conjunction with readOptions. + * Used for writing the options to a tag. Use in conjunction with readOptions. * * @param tag The tag to write the options to - * @return The modified CompoundNBT + * @return The modified CompoundTag */ public default CompoundTag writeOptions(CompoundTag tag) { Option[] customOptions = customOptions(); diff --git a/src/main/java/net/geforcemods/securitycraft/api/Option.java b/src/main/java/net/geforcemods/securitycraft/api/Option.java index 5edd1fb821..c11fc36ed2 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/Option.java +++ b/src/main/java/net/geforcemods/securitycraft/api/Option.java @@ -1,10 +1,14 @@ package net.geforcemods.securitycraft.api; +import java.util.function.Supplier; + import net.geforcemods.securitycraft.misc.TargetingMode; import net.geforcemods.securitycraft.screen.CustomizeBlockScreen; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; /** * A class that allows blocks that have {@link ICustomizable} block entities to have custom, per-block options that are @@ -43,7 +47,11 @@ protected Option(String optionName, T value, T min, T max, T increment) { public abstract void load(CompoundTag tag); - public abstract void save(CompoundTag tag); + public abstract void save(CompoundTag tag, T value); + + public void save(CompoundTag tag) { + save(tag, value); + } public void copy(Option option) { value = (T) option.get(); @@ -52,7 +60,7 @@ public void copy(Option option) { /** * @return This option's name. */ - public final String getName() { + public String getName() { return name; } @@ -131,11 +139,22 @@ public Component getDefaultInfo() { return Component.translatable("securitycraft.option.default_with_range", getDefaultValue(), getMin(), getMax()).withStyle(ChatFormatting.GRAY); } + /** + * @return A textual representation of this option's value + */ + public Component getValueText() { + return Component.literal(toString()); + } + @Override public String toString() { return (value) + ""; } + public EntityDataWrappedOption> wrapForEntityData(EntityDataAccessor entityDataKey, Supplier entityData) { + return new EntityDataWrappedOption<>(this, entityDataKey, entityData); + } + /** * A subclass of {@link Option}, set up to handle booleans. */ @@ -158,7 +177,7 @@ public void load(CompoundTag tag) { } @Override - public void save(CompoundTag tag) { + public void save(CompoundTag tag, Boolean value) { tag.putBoolean(getName(), value); } @@ -166,6 +185,11 @@ public void save(CompoundTag tag) { public Component getDefaultInfo() { return Component.translatable("securitycraft.option.default", Component.translatable(getDefaultValue() ? "gui.securitycraft:invScan.yes" : "gui.securitycraft:invScan.no")).withStyle(ChatFormatting.GRAY); } + + @Override + public Component getValueText() { + return Component.translatable(get() ? "gui.securitycraft:invScan.yes" : "gui.securitycraft:invScan.no"); + } } public static class DisabledOption extends BooleanOption { @@ -232,7 +256,7 @@ public void load(CompoundTag tag) { } @Override - public void save(CompoundTag tag) { + public void save(CompoundTag tag, Integer value) { tag.putInt(getName(), value); } @@ -284,7 +308,7 @@ public void load(CompoundTag tag) { } @Override - public void save(CompoundTag tag) { + public void save(CompoundTag tag, Double value) { tag.putDouble(getName(), value); } @@ -327,17 +351,18 @@ public void load(CompoundTag tag) { } @Override - public void save(CompoundTag tag) { + public void save(CompoundTag tag, T value) { tag.putInt(getName(), value.ordinal()); } - public Component getValueName() { + @Override + public Component getValueText() { return Component.literal(value.name()); } @Override public Component getDefaultInfo() { - return Component.translatable("securitycraft.option.default", getValueName()).withStyle(ChatFormatting.GRAY); + return Component.translatable("securitycraft.option.default", getValueText()).withStyle(ChatFormatting.GRAY); } } @@ -352,8 +377,121 @@ public String getKey(String denotation) { } @Override - public Component getValueName() { + public Component getValueText() { return value.translate(); } } + + public static class EntityDataWrappedOption> extends Option { + private final Option wrapped; + private final EntityDataAccessor entityDataKey; + private final Supplier entityData; + + public EntityDataWrappedOption(O wrapped, EntityDataAccessor entityDataKey, Supplier entityData) { + super(wrapped.getName(), wrapped.getDefaultValue()); + this.wrapped = wrapped; + this.entityDataKey = entityDataKey; + this.entityData = entityData; + } + + @Override + public void toggle() { + wrapped.toggle(); + } + + @Override + public void load(CompoundTag tag) { + wrapped.load(tag); + entityData.get().set(entityDataKey, wrapped.get()); + } + + @Override + public void save(CompoundTag tag, T value) { + wrapped.save(tag, value); + } + + @Override + public void save(CompoundTag tag) { + wrapped.save(tag, entityData.get().get(entityDataKey)); + } + + @Override + public void copy(Option option) { + wrapped.copy(option); + } + + @Override + public final String getName() { + return wrapped.getName(); + } + + @Override + public T get() { + return wrapped.get(); + } + + @Override + public void setValue(T value) { + wrapped.setValue(value); + entityData.get().set(entityDataKey, wrapped.get()); + } + + @Override + public T getDefaultValue() { + return wrapped.getDefaultValue(); + } + + @Override + public T getIncrement() { + return wrapped.getIncrement(); + } + + @Override + public T getMin() { + return wrapped.getMin(); + } + + @Override + public T getMax() { + return wrapped.getMax(); + } + + @Override + public boolean isSlider() { + return wrapped.isSlider(); + } + + @Override + public String getKey(String denotation) { + return wrapped.getKey(denotation); + } + + @Override + public String getDescriptionKey(String denotation) { + return wrapped.getDescriptionKey(denotation); + } + + @Override + public Component getDefaultInfo() { + return wrapped.getDefaultInfo(); + } + + @Override + public Component getValueText() { + return wrapped.getValueText(); + } + + @Override + public String toString() { + return wrapped.toString(); + } + + public Option getWrapped() { + return wrapped; + } + + public EntityDataAccessor getEntityDataKey() { + return entityDataKey; + } + } } \ No newline at end of file diff --git a/src/main/java/net/geforcemods/securitycraft/blocks/mines/ExplosiveBlock.java b/src/main/java/net/geforcemods/securitycraft/blocks/mines/ExplosiveBlock.java index 08a46cbf9e..d97f9f0f5a 100644 --- a/src/main/java/net/geforcemods/securitycraft/blocks/mines/ExplosiveBlock.java +++ b/src/main/java/net/geforcemods/securitycraft/blocks/mines/ExplosiveBlock.java @@ -6,6 +6,7 @@ import net.geforcemods.securitycraft.api.IExplosive; import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.api.Option; +import net.geforcemods.securitycraft.api.Option.EntityDataWrappedOption; import net.geforcemods.securitycraft.api.Option.IgnoreOwnerOption; import net.geforcemods.securitycraft.api.Option.TargetingModeOption; import net.geforcemods.securitycraft.blocks.OwnableBlock; @@ -65,6 +66,9 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player if (level.getBlockEntity(pos) instanceof ICustomizable mine) { for (Option option : mine.customOptions()) { + if (option instanceof EntityDataWrappedOption wrapped) + option = wrapped.getWrapped(); + if (option instanceof TargetingModeOption targetingMode && !targetingMode.get().allowsPlayers()) return InteractionResult.PASS; else if (option instanceof IgnoreOwnerOption ignoreOwner && ((IOwnable) be).isOwnedBy(player) && ignoreOwner.get()) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 34f079e31a..f8d5de6932 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -11,7 +11,7 @@ import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.api.IPasscodeProtected; import net.geforcemods.securitycraft.api.Option; -import net.geforcemods.securitycraft.api.Option.BooleanOption; +import net.geforcemods.securitycraft.api.Option.EntityDataWrappedOption; import net.geforcemods.securitycraft.api.Option.SendAllowlistMessageOption; import net.geforcemods.securitycraft.api.Option.SendDenylistMessageOption; import net.geforcemods.securitycraft.api.Option.SmartModuleCooldownOption; @@ -30,6 +30,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Containers; @@ -52,12 +53,15 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodeProtected, IModuleInventory, ICustomizable { private static final EntityDataAccessor OWNER = SynchedEntityData.defineId(SecuritySeaBoat.class, Owner.getSerializer()); + private static final EntityDataAccessor SEND_ALLOWLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor SEND_DENYLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor SMART_MODULE_COOLDOWN = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.INT); private byte[] passcode; private UUID saltKey; private NonNullList modules = NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY); - private BooleanOption sendAllowlistMessage = new SendAllowlistMessageOption(false); - private BooleanOption sendDenylistMessage = new SendDenylistMessageOption(true); - private SmartModuleCooldownOption smartModuleCooldown = new SmartModuleCooldownOption(); + private EntityDataWrappedOption> sendAllowlistMessage = new SendAllowlistMessageOption(false).wrapForEntityData(SEND_ALLOWLIST_MESSAGE, () -> entityData); + private EntityDataWrappedOption> sendDenylistMessage = new SendDenylistMessageOption(true).wrapForEntityData(SEND_DENYLIST_MESSAGE, () -> entityData); + private EntityDataWrappedOption> smartModuleCooldown = new SmartModuleCooldownOption().wrapForEntityData(SMART_MODULE_COOLDOWN, () -> entityData); private long cooldownEnd = 0; private Map moduleStates = new EnumMap<>(ModuleType.class); @@ -77,6 +81,9 @@ public SecuritySeaBoat(Level level, double x, double y, double z) { protected void defineSynchedData() { super.defineSynchedData(); entityData.define(OWNER, new Owner()); + entityData.define(SEND_ALLOWLIST_MESSAGE, false); + entityData.define(SEND_DENYLIST_MESSAGE, true); + entityData.define(SMART_MODULE_COOLDOWN, 100); } @Override @@ -254,6 +261,32 @@ protected void readAdditionalSaveData(CompoundTag tag) { loadPasscode(tag); } + @Override + public void onOptionChanged(Option option) { + if (!level().isClientSide) { + if (option == sendAllowlistMessage) + entityData.set(SEND_ALLOWLIST_MESSAGE, sendAllowlistMessage.get()); + else if (option == sendDenylistMessage) + entityData.set(SEND_DENYLIST_MESSAGE, sendDenylistMessage.get()); + else if (option == smartModuleCooldown) + entityData.set(SMART_MODULE_COOLDOWN, smartModuleCooldown.get()); + } + } + + @Override + public void onSyncedDataUpdated(EntityDataAccessor key) { + if (level().isClientSide) { + if (key == SEND_ALLOWLIST_MESSAGE) + sendAllowlistMessage.setValue(entityData.get(SEND_ALLOWLIST_MESSAGE)); + else if (key == SEND_DENYLIST_MESSAGE) + sendDenylistMessage.setValue(entityData.get(SEND_DENYLIST_MESSAGE)); + else if (key == SMART_MODULE_COOLDOWN) + smartModuleCooldown.setValue(entityData.get(SMART_MODULE_COOLDOWN)); + } + + super.onSyncedDataUpdated(key); + } + public void setOwner(Player player) { setOwner(player.getGameProfile().getId().toString(), player.getName().getString()); } diff --git a/src/main/java/net/geforcemods/securitycraft/network/server/UpdateSliderValue.java b/src/main/java/net/geforcemods/securitycraft/network/server/UpdateSliderValue.java index ddbcfc9598..c69d244429 100644 --- a/src/main/java/net/geforcemods/securitycraft/network/server/UpdateSliderValue.java +++ b/src/main/java/net/geforcemods/securitycraft/network/server/UpdateSliderValue.java @@ -1,17 +1,18 @@ package net.geforcemods.securitycraft.network.server; import net.geforcemods.securitycraft.SecurityCraft; -import net.geforcemods.securitycraft.api.CustomizableBlockEntity; import net.geforcemods.securitycraft.api.ICustomizable; import net.geforcemods.securitycraft.api.IOwnable; import net.geforcemods.securitycraft.api.Option; import net.geforcemods.securitycraft.api.Option.DoubleOption; +import net.geforcemods.securitycraft.api.Option.EntityDataWrappedOption; import net.geforcemods.securitycraft.api.Option.IntOption; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.network.handling.PlayPayloadContext; @@ -20,6 +21,7 @@ public class UpdateSliderValue implements CustomPacketPayload { private BlockPos pos; private String optionName; private double value; + private int entityId; public UpdateSliderValue() {} @@ -29,15 +31,33 @@ public UpdateSliderValue(BlockPos pos, Option option, double v) { value = v; } + public UpdateSliderValue(int entityId, Option option, double v) { + this.entityId = entityId; + optionName = option.getName(); + value = v; + } + public UpdateSliderValue(FriendlyByteBuf buf) { - pos = buf.readBlockPos(); + if (buf.readBoolean()) + pos = buf.readBlockPos(); + else + entityId = buf.readVarInt(); + optionName = buf.readUtf(); value = buf.readDouble(); } @Override public void write(FriendlyByteBuf buf) { - buf.writeBlockPos(pos); + boolean hasPos = pos != null; + + buf.writeBoolean(hasPos); + + if (hasPos) + buf.writeBlockPos(pos); + else + buf.writeVarInt(entityId); + buf.writeUtf(optionName); buf.writeDouble(value); } @@ -49,9 +69,15 @@ public ResourceLocation id() { public void handle(PlayPayloadContext ctx) { Player player = ctx.player().orElseThrow(); - BlockEntity be = player.level().getBlockEntity(pos); + Level level = player.level(); + ICustomizable customizable = null; + + if (pos != null && level.getBlockEntity(pos) instanceof ICustomizable be) + customizable = be; + else if (pos == null && level.getEntity(entityId) instanceof ICustomizable entity) + customizable = entity; - if (be instanceof ICustomizable customizable && (!(be instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { + if (customizable != null && (!(customizable instanceof IOwnable ownable) || ownable.isOwnedBy(player))) { Option option = null; for (Option o : customizable.customOptions()) { @@ -64,15 +90,23 @@ public void handle(PlayPayloadContext ctx) { if (option == null) return; - if (option instanceof DoubleOption o) + if (option instanceof EntityDataWrappedOption o) { + Option wrapped = o.getWrapped(); + + if (wrapped instanceof DoubleOption) + o.setValue(value); + else if (wrapped instanceof IntOption) + o.setValue((int) value); + } + else if (option instanceof DoubleOption o) o.setValue(value); else if (option instanceof IntOption o) o.setValue((int) value); customizable.onOptionChanged(option); - if (be instanceof CustomizableBlockEntity) - player.level().sendBlockUpdated(pos, be.getBlockState(), be.getBlockState(), 3); + if (customizable instanceof BlockEntity be) + level.sendBlockUpdated(pos, be.getBlockState(), be.getBlockState(), 3); } } } diff --git a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java index 1b66d2d08d..14a4350115 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java @@ -7,9 +7,8 @@ import net.geforcemods.securitycraft.api.ICustomizable; import net.geforcemods.securitycraft.api.IModuleInventory; import net.geforcemods.securitycraft.api.Option; -import net.geforcemods.securitycraft.api.Option.BooleanOption; import net.geforcemods.securitycraft.api.Option.DoubleOption; -import net.geforcemods.securitycraft.api.Option.EnumOption; +import net.geforcemods.securitycraft.api.Option.EntityDataWrappedOption; import net.geforcemods.securitycraft.api.Option.IntOption; import net.geforcemods.securitycraft.inventory.CustomizeBlockMenu; import net.geforcemods.securitycraft.items.ModuleItem; @@ -93,7 +92,7 @@ public void init() { optionButtons = new AbstractWidget[options.length]; for (int i = 0; i < options.length; i++) { - Option option = options[i]; + Option option = options[i] instanceof EntityDataWrappedOption wrapped ? wrapped.getWrapped() : options[i]; if (option.isSlider()) { if (option instanceof DoubleOption doubleOption) { @@ -102,7 +101,11 @@ public void init() { optionButtons[i] = new CallbackSlider(leftPos + 178, (topPos + 10) + (i * 25), 120, 20, Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), ""), Component.empty(), doubleOption.getMin(), doubleOption.getMax(), doubleOption.get(), doubleOption.getIncrement(), 0, true, slider -> { doubleOption.setValue(slider.getValue()); optionButtons[sliderIndex].setTooltip(Tooltip.create(getOptionDescription(sliderIndex))); - PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.myPos(), option, doubleOption.get())); + + if (menu.entityId == -1) + PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.myPos(), option, doubleOption.get())); + else + PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(menu.entityId, option, doubleOption.get())); }); } else if (option instanceof IntOption intOption) { @@ -111,7 +114,11 @@ else if (option instanceof IntOption intOption) { optionButtons[i] = new CallbackSlider(leftPos + 178, (topPos + 10) + (i * 25), 120, 20, Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), ""), Component.empty(), intOption.getMin(), intOption.getMax(), intOption.get(), true, slider -> { intOption.setValue(slider.getValueInt()); optionButtons[sliderIndex].setTooltip(Tooltip.create(getOptionDescription(sliderIndex))); - PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.myPos(), option, intOption.get())); + + if (menu.entityId == -1) + PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(moduleInv.myPos(), option, intOption.get())); + else + PacketDistributor.SERVER.noArg().send(new UpdateSliderValue(menu.entityId, option, intOption.get())); }); } @@ -239,20 +246,11 @@ private Component getModuleTooltipText(int moduleId) { private Component getOptionDescription(int optionId) { Option option = ((ICustomizable) moduleInv).customOptions()[optionId]; - return Utils.localize("gui.securitycraft:customize.tooltip", Component.translatable(option.getDescriptionKey(BlockUtils.getLanguageKeyDenotation(moduleInv))), Component.translatable("gui.securitycraft:customize.currentSetting", getValueText(option))); + return Utils.localize("gui.securitycraft:customize.tooltip", Component.translatable(option.getDescriptionKey(BlockUtils.getLanguageKeyDenotation(moduleInv))), Component.translatable("gui.securitycraft:customize.currentSetting", option.getValueText())); } private Component getOptionButtonTitle(Option option) { - return Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), getValueText(option)); - } - - private Component getValueText(Option option) { - if (option instanceof BooleanOption booleanOption) - return Component.translatable(booleanOption.get() ? "gui.securitycraft:invScan.yes" : "gui.securitycraft:invScan.no"); - else if (option instanceof EnumOption enumOption) - return enumOption.getValueName(); - else - return Component.literal(option.toString()); + return Utils.localize(option.getKey(BlockUtils.getLanguageKeyDenotation(moduleInv)), option.getValueText()); } @Override From fd09b72d0f3569b110fd3feb32ddcea7e196098a Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 11:56:05 +0200 Subject: [PATCH 33/55] start fixing module states not synchronizing --- .../geforcemods/securitycraft/SCContent.java | 3 ++ .../securitycraft/entity/SecuritySeaBoat.java | 10 +++-- .../misc/ModuleStatesSerializer.java | 41 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/geforcemods/securitycraft/misc/ModuleStatesSerializer.java diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index a17bd4088c..389ad17364 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; import com.google.common.base.Predicates; @@ -231,6 +232,7 @@ import net.geforcemods.securitycraft.items.WireCuttersItem; import net.geforcemods.securitycraft.misc.BlockEntityNBTCondition; import net.geforcemods.securitycraft.misc.LimitedUseKeycardRecipe; +import net.geforcemods.securitycraft.misc.ModuleStatesSerializer; import net.geforcemods.securitycraft.misc.ModuleType; import net.geforcemods.securitycraft.misc.OwnerDataSerializer; import net.geforcemods.securitycraft.misc.PageGroup; @@ -322,6 +324,7 @@ public class SCContent { //data serializer entries public static final DeferredHolder, EntityDataSerializer> OWNER_SERIALIZER = DATA_SERIALIZERS.register("owner", () -> new OwnerDataSerializer()); + public static final DeferredHolder, EntityDataSerializer>> MODULE_STATES_SERIALIZER = DATA_SERIALIZERS.register("module_states", () -> new ModuleStatesSerializer()); //particle types public static final DeferredHolder, SimpleParticleType> FLOOR_TRAP_CLOUD = PARTICLE_TYPES.register("floor_trap_cloud", () -> new SimpleParticleType(false)); diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index f8d5de6932..05af4f1170 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -56,6 +56,7 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodePro private static final EntityDataAccessor SEND_ALLOWLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor SEND_DENYLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor SMART_MODULE_COOLDOWN = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.INT); + private static final EntityDataAccessor> MODULE_STATES = SynchedEntityData.>defineId(SecuritySeaBoat.class, SCContent.MODULE_STATES_SERIALIZER.get()); private byte[] passcode; private UUID saltKey; private NonNullList modules = NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY); @@ -63,7 +64,6 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodePro private EntityDataWrappedOption> sendDenylistMessage = new SendDenylistMessageOption(true).wrapForEntityData(SEND_DENYLIST_MESSAGE, () -> entityData); private EntityDataWrappedOption> smartModuleCooldown = new SmartModuleCooldownOption().wrapForEntityData(SMART_MODULE_COOLDOWN, () -> entityData); private long cooldownEnd = 0; - private Map moduleStates = new EnumMap<>(ModuleType.class); public SecuritySeaBoat(EntityType type, Level level) { super(SCContent.SECURITY_SEA_BOAT_ENTITY.get(), level); @@ -84,6 +84,7 @@ protected void defineSynchedData() { entityData.define(SEND_ALLOWLIST_MESSAGE, false); entityData.define(SEND_DENYLIST_MESSAGE, true); entityData.define(SMART_MODULE_COOLDOWN, 100); + entityData.define(MODULE_STATES, new EnumMap<>(ModuleType.class)); } @Override @@ -253,7 +254,7 @@ protected void addAdditionalSaveData(CompoundTag tag) { protected void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); modules = readModuleInventory(tag); - moduleStates = readModuleStates(tag); + entityData.set(MODULE_STATES, readModuleStates(tag)); readOptions(tag); cooldownEnd = System.currentTimeMillis() + tag.getLong("cooldownLeft"); entityData.set(OWNER, Owner.fromCompound(tag.getCompound("owner"))); @@ -367,12 +368,15 @@ public ModuleType[] acceptedModules() { @Override public boolean isModuleEnabled(ModuleType module) { - return hasModule(module) && moduleStates.get(module) == Boolean.TRUE; //prevent NPE + return hasModule(module) && entityData.get(MODULE_STATES).get(module) == Boolean.TRUE; //prevent NPE } @Override public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { + Map moduleStates = entityData.get(MODULE_STATES); + moduleStates.put(module, shouldBeEnabled); + entityData.set(MODULE_STATES, moduleStates); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/misc/ModuleStatesSerializer.java b/src/main/java/net/geforcemods/securitycraft/misc/ModuleStatesSerializer.java new file mode 100644 index 0000000000..4a957079c9 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/misc/ModuleStatesSerializer.java @@ -0,0 +1,41 @@ +package net.geforcemods.securitycraft.misc; + +import java.util.EnumMap; +import java.util.Map; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializer; + +public class ModuleStatesSerializer implements EntityDataSerializer> { + @Override + public void write(FriendlyByteBuf buf, Map value) { + buf.writeVarInt(value.size()); + value.forEach((k, v) -> { + buf.writeEnum(k); + buf.writeBoolean(v); + }); + } + + @Override + public Map read(FriendlyByteBuf buf) { + Map moduleStates = new EnumMap<>(ModuleType.class); + int size = buf.readVarInt(); + + for (int i = 0; i < size; i++) { + moduleStates.put(buf.readEnum(ModuleType.class), buf.readBoolean()); + } + + return moduleStates; + } + + @Override + public EntityDataAccessor> createAccessor(int id) { + return new EntityDataAccessor<>(id, this); + } + + @Override + public Map copy(Map value) { + return new EnumMap<>(value); + } +} From b13b08143bbfac9945970c0f5b580f68cb61d3d9 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 15:46:55 +0200 Subject: [PATCH 34/55] make boat move very slowly in lava and set player on fire --- .../securitycraft/api/ICustomizable.java | 2 +- .../securitycraft/entity/SecuritySeaBoat.java | 46 +++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java b/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java index e8c1921d69..068a8d421b 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java +++ b/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java @@ -10,7 +10,7 @@ */ public interface ICustomizable { /** - * @return An array of what custom {@link Option}s this bject has. + * @return An array of what custom {@link Option}s this object has. */ public Option[] customOptions(); diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 05af4f1170..14cddf8cc9 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -49,6 +49,11 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.common.NeoForgeMod; +import net.neoforged.neoforge.fluids.FluidType; import net.neoforged.neoforge.network.PacketDistributor; public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodeProtected, IModuleInventory, ICustomizable { @@ -64,6 +69,7 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodePro private EntityDataWrappedOption> sendDenylistMessage = new SendDenylistMessageOption(true).wrapForEntityData(SEND_DENYLIST_MESSAGE, () -> entityData); private EntityDataWrappedOption> smartModuleCooldown = new SmartModuleCooldownOption().wrapForEntityData(SMART_MODULE_COOLDOWN, () -> entityData); private long cooldownEnd = 0; + private boolean isInLava = false; public SecuritySeaBoat(EntityType type, Level level) { super(SCContent.SECURITY_SEA_BOAT_ENTITY.get(), level); @@ -185,6 +191,46 @@ public void openSetPasscodeScreen(ServerPlayer player, BlockPos pos) { PacketDistributor.PLAYER.with(player).send(new OpenScreen(DataType.SET_PASSCODE_FOR_ENTITY, getId())); } + @Override + public boolean canBoatInFluid(FluidState state) { + return super.canBoatInFluid(state) || state.is(Fluids.LAVA); + } + + @Override + public boolean canBoatInFluid(FluidType type) { + return super.canBoatInFluid(type) || type == NeoForgeMod.LAVA_TYPE; + } + + @Override + public boolean checkInWater() { + isInLava = level().getFluidState(blockPosition()).is(Fluids.LAVA); + return super.checkInWater(); + } + + @Override + public void setDeltaMovement(Vec3 deltaMovement) { + if (isInLava) + super.setDeltaMovement(deltaMovement.scale(0.5F)); + else + super.setDeltaMovement(deltaMovement); + } + + @Override + public void tick() { + super.tick(); + + if (!level().isClientSide && isInLava) { + Entity passenger = getFirstPassenger(); + + if (passenger != null && !passenger.fireImmune()) { + passenger.setRemainingFireTicks(passenger.getRemainingFireTicks() + 1); + + if (passenger.getRemainingFireTicks() == 0) + passenger.setSecondsOnFire(8); + } + } + } + @Override public boolean hurt(DamageSource source, float amount) { Entity entity = source.getEntity(); diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 8a6c9f0abb..c56bb3b7d8 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -69,3 +69,4 @@ public net.minecraft.server.commands.FillCommand ERROR_AREA_TOO_LARGE public net.minecraft.world.item.BoatItem getBoat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/phys/HitResult;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/player/Player;)Lnet/minecraft/world/entity/vehicle/Boat; public-f net.minecraft.client.renderer.entity.BoatRenderer boatResources public net.minecraft.client.renderer.entity.BoatRenderer createBoatModel(Lnet/minecraft/client/renderer/entity/EntityRendererProvider$Context;Lnet/minecraft/world/entity/vehicle/Boat$Type;Z)Lnet/minecraft/client/model/ListModel; +public net.minecraft.world.entity.vehicle.Boat checkInWater()Z From 21458733f085542108af389c49050f9b9cc926cd Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 15:51:04 +0200 Subject: [PATCH 35/55] make boat item fire reisistant --- .../geforcemods/securitycraft/SCContent.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java index 389ad17364..164d291dbe 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCContent.java +++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java @@ -2635,23 +2635,23 @@ public class SCContent { @HasManualPage(PageGroup.SECRET_HANGING_SIGNS) public static final DeferredItem SECRET_WARPED_HANGING_SIGN_ITEM = ITEMS.register("secret_warped_hanging_sign", () -> new HangingSignItem(SCContent.SECRET_WARPED_HANGING_SIGN.get(), SCContent.SECRET_WARPED_WALL_HANGING_SIGN.get(), itemProp(16))); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem OAK_SECURITY_SEA_BOAT = ITEMS.register("oak_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.OAK, itemProp(1))); + public static final DeferredItem OAK_SECURITY_SEA_BOAT = ITEMS.register("oak_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.OAK, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem SPRUCE_SECURITY_SEA_BOAT = ITEMS.register("spruce_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.SPRUCE, itemProp(1))); + public static final DeferredItem SPRUCE_SECURITY_SEA_BOAT = ITEMS.register("spruce_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.SPRUCE, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem BIRCH_SECURITY_SEA_BOAT = ITEMS.register("birch_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.BIRCH, itemProp(1))); + public static final DeferredItem BIRCH_SECURITY_SEA_BOAT = ITEMS.register("birch_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.BIRCH, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem JUNGLE_SECURITY_SEA_BOAT = ITEMS.register("jungle_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.JUNGLE, itemProp(1))); + public static final DeferredItem JUNGLE_SECURITY_SEA_BOAT = ITEMS.register("jungle_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.JUNGLE, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem ACACIA_SECURITY_SEA_BOAT = ITEMS.register("acacia_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.ACACIA, itemProp(1))); + public static final DeferredItem ACACIA_SECURITY_SEA_BOAT = ITEMS.register("acacia_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.ACACIA, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem DARK_OAK_SECURITY_SEA_BOAT = ITEMS.register("dark_oak_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.DARK_OAK, itemProp(1))); + public static final DeferredItem DARK_OAK_SECURITY_SEA_BOAT = ITEMS.register("dark_oak_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.DARK_OAK, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem MANGROVE_SECURITY_SEA_BOAT = ITEMS.register("mangrove_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.MANGROVE, itemProp(1))); + public static final DeferredItem MANGROVE_SECURITY_SEA_BOAT = ITEMS.register("mangrove_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.MANGROVE, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem CHERRY_SECURITY_SEA_BOAT = ITEMS.register("cherry_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.CHERRY, itemProp(1))); + public static final DeferredItem CHERRY_SECURITY_SEA_BOAT = ITEMS.register("cherry_security_sea_boat", () -> new SecuritySeaBoatItem(Boat.Type.CHERRY, itemProp(1).fireResistant())); @HasManualPage(PageGroup.SECURITY_SEA_BOATS) - public static final DeferredItem BAMBOO_SECURITY_SEA_RAFT = ITEMS.register("bamboo_security_sea_raft", () -> new SecuritySeaBoatItem(Boat.Type.BAMBOO, itemProp(1))); + public static final DeferredItem BAMBOO_SECURITY_SEA_RAFT = ITEMS.register("bamboo_security_sea_raft", () -> new SecuritySeaBoatItem(Boat.Type.BAMBOO, itemProp(1).fireResistant())); @HasManualPage(designedBy = "Henzoid") public static final DeferredItem SENTRY = ITEMS.register("sentry", () -> new SentryItem(itemProp())); public static final DeferredItem SONIC_SECURITY_SYSTEM_ITEM = ITEMS.register("sonic_security_system", () -> new SonicSecuritySystemItem(itemProp(1))); From 89b379361fbadefb12824e0180ae52376a73bdf0 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 16:40:04 +0200 Subject: [PATCH 36/55] update sc manual entry and add module lines --- .../geforcemods/securitycraft/entity/SecuritySeaBoat.java | 5 +++++ src/main/resources/assets/securitycraft/lang/de_at.json | 6 +++++- src/main/resources/assets/securitycraft/lang/de_ch.json | 6 +++++- src/main/resources/assets/securitycraft/lang/de_de.json | 6 +++++- src/main/resources/assets/securitycraft/lang/en_us.json | 6 +++++- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 14cddf8cc9..5f2f9b3aaf 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -425,6 +425,11 @@ public void toggleModuleState(ModuleType module, boolean shouldBeEnabled) { entityData.set(MODULE_STATES, moduleStates); } + @Override + public String getModuleDescriptionId(String denotation, ModuleType module) { + return IModuleInventory.super.getModuleDescriptionId("generic." + denotation, module); + } + @Override public Level myLevel() { return level(); diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index 4bb79e7a77..a172a0c292 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -897,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann jedoch nicht auf Lava schwimmen.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -1106,6 +1106,10 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Ein Zulassungslisten-Modul in einem verstärkten Zauntor erlaubt es zugelassenen Spielern, dieses zu öffnen.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Ein Zulassungslisten-Modul in einer verstärkten Druckplatte erlaubt es zugelassenen Spielern, diese zu aktivieren.", "module.generic.secret_sign.whitelist_module.description": "Ein Zulassungslisten-Modul in einem geheimen Schild erlaubt es zugelassenen Spielern, dessen Text zu sehen und mit dem Schild zu interagieren.", + "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Schloss interagieren können.", + "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", + "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", + "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", "module.securitycraft.alarm.smart_module.description": "Ein schlaues Modul in einem Alarm lässt dich einstellen, welches Geräusch der Alarm abspielt, wenn er aktiviert wird.", "module.securitycraft.block_change_detector.disguise_module.description": "Wenn ein Tarnmodul zu einem Block-Änderungsdetektor hinzugefügt wird, ändert es seine Textur/sein Modell zu denen des Blockes im Tarnmodul.", "module.securitycraft.block_change_detector.redstone_module.description": "Ein Redstone-Modul in einem Block-Änderungsdetektor lässt ihn ein Redstonesignal abgeben, wenn ein Spieler einen Block in seiner Nähte platziert/zerbricht.", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index 4bb79e7a77..a172a0c292 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -897,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann jedoch nicht auf Lava schwimmen.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -1106,6 +1106,10 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Ein Zulassungslisten-Modul in einem verstärkten Zauntor erlaubt es zugelassenen Spielern, dieses zu öffnen.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Ein Zulassungslisten-Modul in einer verstärkten Druckplatte erlaubt es zugelassenen Spielern, diese zu aktivieren.", "module.generic.secret_sign.whitelist_module.description": "Ein Zulassungslisten-Modul in einem geheimen Schild erlaubt es zugelassenen Spielern, dessen Text zu sehen und mit dem Schild zu interagieren.", + "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Schloss interagieren können.", + "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", + "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", + "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", "module.securitycraft.alarm.smart_module.description": "Ein schlaues Modul in einem Alarm lässt dich einstellen, welches Geräusch der Alarm abspielt, wenn er aktiviert wird.", "module.securitycraft.block_change_detector.disguise_module.description": "Wenn ein Tarnmodul zu einem Block-Änderungsdetektor hinzugefügt wird, ändert es seine Textur/sein Modell zu denen des Blockes im Tarnmodul.", "module.securitycraft.block_change_detector.redstone_module.description": "Ein Redstone-Modul in einem Block-Änderungsdetektor lässt ihn ein Redstonesignal abgeben, wenn ein Spieler einen Block in seiner Nähte platziert/zerbricht.", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index 4bb79e7a77..a172a0c292 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -897,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann jedoch nicht auf Lava schwimmen.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -1106,6 +1106,10 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Ein Zulassungslisten-Modul in einem verstärkten Zauntor erlaubt es zugelassenen Spielern, dieses zu öffnen.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Ein Zulassungslisten-Modul in einer verstärkten Druckplatte erlaubt es zugelassenen Spielern, diese zu aktivieren.", "module.generic.secret_sign.whitelist_module.description": "Ein Zulassungslisten-Modul in einem geheimen Schild erlaubt es zugelassenen Spielern, dessen Text zu sehen und mit dem Schild zu interagieren.", + "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Schloss interagieren können.", + "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", + "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", + "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", "module.securitycraft.alarm.smart_module.description": "Ein schlaues Modul in einem Alarm lässt dich einstellen, welches Geräusch der Alarm abspielt, wenn er aktiviert wird.", "module.securitycraft.block_change_detector.disguise_module.description": "Wenn ein Tarnmodul zu einem Block-Änderungsdetektor hinzugefügt wird, ändert es seine Textur/sein Modell zu denen des Blockes im Tarnmodul.", "module.securitycraft.block_change_detector.redstone_module.description": "Ein Redstone-Modul in einem Block-Änderungsdetektor lässt ihn ein Redstonesignal abgeben, wenn ein Spieler einen Block in seiner Nähte platziert/zerbricht.", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index a22661b617..641ac80986 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -905,7 +905,7 @@ "help.securitycraft.scanner_trapdoor.info": "The Scanner Trapdoor acts like a Retinal Scanner combined with a Reinforced Trapdoor, where the owner looks at the trapdoor to open it.", "help.securitycraft.secret_signs.info": "The Secret Sign works like a vanilla sign, with the slight difference that only the owner of the sign and players on the allowlist can interact with, and view the text on it. Make sure no one can break the block it's placed on!", "help.securitycraft.security_camera.info": "The security camera allows you to view the nearby area from its position. Simply place down a camera, then rightclick it with a camera monitor to bind it for use.", - "help.securitycraft.security_sea_boats.info": "The security sea boat is a chest boat with a Passcode-protected Chest. Anyone who has the correct code can open the chest, but only the owner can break the boat. The boat is immune to any damage, even lava. It cannot swim on lava, however.", + "help.securitycraft.security_sea_boats.info": "The security sea boat is a chest boat with a Passcode-protected Chest. Anyone who has the correct code can open the chest, but only the owner can break the boat. The boat is immune to any damage, even lava. While it is able to swim on lava, it is very slow and the passenger will take damage.", "help.securitycraft.sentry.info": "The Sentry attacks intruders with an infinite supply of bullets. Alternatively, other projectiles can be used by putting them in a passcode-protected chest or barrel below the Sentry. It has three modes and three target types that can be switched by rightclicking it. The modes are \"Idle\" (it will never attack), \"Camouflage\" (it will only activate once a target is within range) and \"Aggressive\" (it will always be active), with the latter two modes being available with the target types \"Hostile mobs only\", \"Hostile mobs and players\", and \"Players only\". A disguise module will make the Sentry disguise itself with the block inserted in the module, a speed module will increase the Sentry's attack speed, and an allowlist module will prohibit it from shooting listed players. The modules can be inserted or switched out by rightclicking a Sentry, and they can be extracted by using a Universal Block Modifier. To remove the Sentry, sneak-rightclick it.", "help.securitycraft.smart_module.info": "The smart module upgrades the range or functions of a block.", "help.securitycraft.sonic_security_system.info": "The Sonic Security System, inspired by the similarly-named device in the game \"Metroid Prime 2: Echoes\", is able to lock certain SecurityCraft blocks and prevent them from being used or interacted with. Right-clicking a supported block will bind it to the Sonic Security System. Once it is placed down, right-clicking it will open a GUI where you can enable, disable, or reset the Sonic Security System. The blocks can be unlocked by playing a tune nearby, either via the Portable Tune Player, or note blocks. To set such a tune, toggle note recording on in the Sonic Security System's GUI then play the tune that you want using note blocks played nearby. Toggle the recording off once you are finished. Any time that tune is played nearby the Sonic Security System, blocks locked by it will become accessible for a short while.", @@ -1115,6 +1115,10 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Adding an allowlist module to a reinforced fence gate will allow listed players to open it.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Adding an allowlist module to a reinforced pressure plate will allow listed players to activate it.", "module.generic.secret_sign.whitelist_module.description": "Adding an allowlist module to a secret sign will allow listed players to interact with, and view the text on it.", + "module.generic.security_sea_boat.blacklist_module.description": "Adding a denylist module to a Security Sea Boat will ban listed players from interacting with the boat.", + "module.generic.security_sea_boat.harming_module.description": "Adding a harming module to a Security Sea Boat will result in the player getting damaged, if they enter an incorrect code.", + "module.generic.security_sea_boat.smart_module.description": "Adding a smart module to a Security Sea Boat will result in a cooldown being applied after an incorrect code has been entered. During that time, no new code can be entered.", + "module.generic.security_sea_boat.whitelist_module.description": "Adding an allowlist module to a Security Sea Boat will allow players to open it without knowing the code. Additionally, Reinforced Hoppers owned by listed players can extract items from the chest.", "module.securitycraft.alarm.smart_module.description": "Adding a smart module to an alarm will allow you to select which sound the alarm plays when it is activated.", "module.securitycraft.block_change_detector.disguise_module.description": "Adding a disguise module to a block change detector will cause the texture & model of it to change to the texture & model of the block that is added to the disguise module.", "module.securitycraft.block_change_detector.redstone_module.description": "Adding a redstone module to a block change detector makes it emit a redstone signal each time it detects a block was changed by a player in its vicinity.", From 8276cd34ed5490d4dd1936482d99d349c204a1a9 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 17:02:31 +0200 Subject: [PATCH 37/55] fix boat's sc manual page --- .../securitycraft/entity/SecuritySeaBoat.java | 3 +- .../securitycraft/misc/SCManualPage.java | 22 ++++- .../securitycraft/screen/SCManualScreen.java | 85 +++++++++---------- .../assets/securitycraft/lang/de_at.json | 6 +- .../assets/securitycraft/lang/de_ch.json | 6 +- .../assets/securitycraft/lang/de_de.json | 6 +- .../assets/securitycraft/lang/en_us.json | 6 +- 7 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 5f2f9b3aaf..b558a232c7 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -335,7 +335,8 @@ else if (key == SMART_MODULE_COOLDOWN) } public void setOwner(Player player) { - setOwner(player.getGameProfile().getId().toString(), player.getName().getString()); + if (player != null) + setOwner(player.getGameProfile().getId().toString(), player.getName().getString()); } @Override diff --git a/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java b/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java index bf2cb49f9d..631b2980b4 100644 --- a/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java +++ b/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java @@ -1,6 +1,26 @@ package net.geforcemods.securitycraft.misc; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.BoatItem; import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; -public record SCManualPage(Item item, PageGroup group, Component title, Component helpInfo, String designedBy, boolean hasRecipeDescription) {} +public record SCManualPage(Item item, PageGroup group, Component title, Component helpInfo, String designedBy, boolean hasRecipeDescription) { + public Object getInWorldObject() { + if (item instanceof BlockItem blockItem) { + Block block = blockItem.getBlock(); + + return ((EntityBlock) block).newBlockEntity(BlockPos.ZERO, block.defaultBlockState()); + } + else if (item instanceof BoatItem boatItem) + return boatItem.getBoat(null, BlockHitResult.miss(Vec3.ZERO, Direction.NORTH, BlockPos.ZERO), item.getDefaultInstance(), null); + else + return null; + } +} diff --git a/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java index f7c6e8eb24..c6a1dd29b6 100644 --- a/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java +++ b/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java @@ -43,7 +43,6 @@ import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.resources.language.I18n; -import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.core.RegistryAccess; import net.minecraft.network.chat.ClickEvent; @@ -63,8 +62,6 @@ import net.minecraft.world.item.crafting.ShapelessRecipe; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.client.gui.widget.ScrollPanel; @@ -475,66 +472,66 @@ else if (recipe != null) { if (explosive) hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 107, (startX + 107) + 16, Utils.localize("gui.securitycraft:scManual.explosiveBlock"))); + } - if (block.defaultBlockState().hasBlockEntity()) { - BlockEntity be = ((EntityBlock) block).newBlockEntity(BlockPos.ZERO, block.defaultBlockState()); - - ownable = be instanceof IOwnable; - passcodeProtected = be instanceof IPasscodeProtected; - viewActivated = be instanceof IViewActivated; - lockable = be instanceof ILockable; - - if (ownable) - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 29, (startX + 29) + 16, Utils.localize("gui.securitycraft:scManual.ownableBlock"))); - - if (passcodeProtected) - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 55, (startX + 55) + 16, Utils.localize("gui.securitycraft:scManual.passcodeProtectedBlock"))); - - if (viewActivated) - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 81, (startX + 81) + 16, Utils.localize("gui.securitycraft:scManual.viewActivatedBlock"))); + Object inWorldObject = page.getInWorldObject(); - if (be instanceof ICustomizable customizableBe) { - Option[] options = customizableBe.customOptions(); + if (inWorldObject != null) { + ownable = inWorldObject instanceof IOwnable; + passcodeProtected = inWorldObject instanceof IPasscodeProtected; + viewActivated = inWorldObject instanceof IViewActivated; + lockable = inWorldObject instanceof ILockable; - if (options.length > 0) { - List display = new ArrayList<>(); + if (ownable) + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 29, (startX + 29) + 16, Utils.localize("gui.securitycraft:scManual.ownableBlock"))); - hasOptions = true; - display.add(Utils.localize("gui.securitycraft:scManual.options")); - display.add(Component.literal("---")); + if (passcodeProtected) + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 55, (startX + 55) + 16, Utils.localize("gui.securitycraft:scManual.passcodeProtectedBlock"))); - for (Option option : options) { - display.add(Component.translatable("gui.securitycraft:scManual.option_text", Component.translatable(option.getDescriptionKey(BlockUtils.getLanguageKeyDenotation(customizableBe))), option.getDefaultInfo())); - display.add(Component.empty()); - } + if (viewActivated) + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 81, (startX + 81) + 16, Utils.localize("gui.securitycraft:scManual.viewActivatedBlock"))); - display.remove(display.size() - 1); - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 136, (startX + 136) + 16, display)); - } - } + if (inWorldObject instanceof ICustomizable customizableObj) { + Option[] options = customizableObj.customOptions(); - if (be instanceof IModuleInventory moduleInv && moduleInv.acceptedModules() != null && moduleInv.acceptedModules().length > 0) { + if (options.length > 0) { List display = new ArrayList<>(); - hasModules = true; - display.add(Utils.localize("gui.securitycraft:scManual.modules")); + hasOptions = true; + display.add(Utils.localize("gui.securitycraft:scManual.options")); display.add(Component.literal("---")); - for (ModuleType module : moduleInv.acceptedModules()) { - display.add(Component.literal("- ").append(Utils.localize(moduleInv.getModuleDescriptionId(BlockUtils.getLanguageKeyDenotation(moduleInv), module)))); + for (Option option : options) { + display.add(Component.translatable("gui.securitycraft:scManual.option_text", Component.translatable(option.getDescriptionKey(BlockUtils.getLanguageKeyDenotation(customizableObj))), option.getDefaultInfo())); display.add(Component.empty()); } display.remove(display.size() - 1); - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 163, (startX + 163) + 16, display)); + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 136, (startX + 136) + 16, display)); } + } - if (lockable) - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 189, startX + 189 + 16, Utils.localize("gui.securitycraft:scManual.lockable"))); + if (inWorldObject instanceof IModuleInventory moduleInv && moduleInv.acceptedModules() != null && moduleInv.acceptedModules().length > 0) { + List display = new ArrayList<>(); - if (hasOptions || hasModules) - hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 213, (startX + 213) + 16, Utils.localize("gui.securitycraft:scManual.customizableBlock"))); + hasModules = true; + display.add(Utils.localize("gui.securitycraft:scManual.modules")); + display.add(Component.literal("---")); + + for (ModuleType module : moduleInv.acceptedModules()) { + display.add(Component.literal("- ").append(Utils.localize(moduleInv.getModuleDescriptionId(BlockUtils.getLanguageKeyDenotation(moduleInv), module)))); + display.add(Component.empty()); + } + + display.remove(display.size() - 1); + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 163, (startX + 163) + 16, display)); } + + if (lockable) + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 189, startX + 189 + 16, Utils.localize("gui.securitycraft:scManual.lockable"))); + + if (hasOptions || hasModules) + hoverCheckers.add(new TextHoverChecker(118, 118 + 16, startX + 213, (startX + 213) + 16, Utils.localize("gui.securitycraft:scManual.customizableBlock"))); } if (recipe != null && !recipe.isEmpty()) { diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index a172a0c292..3f1d1c1390 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -747,7 +747,7 @@ "gui.securitycraft:scManual.author": "Übersetzt von bl4ckscor3 und Redstone_Dubstep", "gui.securitycraft:scManual.block_mines": "Blockminen", "gui.securitycraft:scManual.block_reinforcers": "Universelle Blockverstärker", - "gui.securitycraft:scManual.customizableBlock": "Anpassbar: Bei diesem Block kannst du den Universellen Blockmodifizierer verwenden, um die Funktionen des Blockes anzupassen.", + "gui.securitycraft:scManual.customizableBlock": "Anpassbar: Du kannst den Universellen Blockmodifizierer verwenden, um die Funktionen hiervon anzupassen.", "gui.securitycraft:scManual.designedBy": "Entworfen von: %s", "gui.securitycraft:scManual.disabled": "Deaktiviert", "gui.securitycraft:scManual.display_cases": "Vitrinen", @@ -758,8 +758,8 @@ "gui.securitycraft:scManual.lockable": "Sperrbar: Dieser Block kann durch ein Schallsicherheitssystem gesperrt werden.", "gui.securitycraft:scManual.modules": "Verfügbare Module:", "gui.securitycraft:scManual.options": "Verfügbare Einstellungen:", - "gui.securitycraft:scManual.ownableBlock": "Besitzbar: Dieser Block ist resistent gegenüber Explosionen und kann nur vom Besitzer mit einem Universellen Blockentferner entfernt werden.", - "gui.securitycraft:scManual.passcodeProtectedBlock": "Passwortgesichert: Dieser Block kann nur benutzt werden, wenn das korrekte Passwort eingegeben wird.", + "gui.securitycraft:scManual.ownableBlock": "Besitzbar: Dies ist resistent gegenüber Explosionen und kann nur vom Besitzer mit einem Universellen Blockentferner entfernt werden.", + "gui.securitycraft:scManual.passcodeProtectedBlock": "Passwortgesichert: Dies kann nur benutzt werden, wenn das korrekte Passwort eingegeben wird.", "gui.securitycraft:scManual.patreon.error": "Fehler beim Laden der Patrons!", "gui.securitycraft:scManual.patreon.loading": "Lade...", "gui.securitycraft:scManual.patreon.title": "Unsere Patrons:", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index a172a0c292..3f1d1c1390 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -747,7 +747,7 @@ "gui.securitycraft:scManual.author": "Übersetzt von bl4ckscor3 und Redstone_Dubstep", "gui.securitycraft:scManual.block_mines": "Blockminen", "gui.securitycraft:scManual.block_reinforcers": "Universelle Blockverstärker", - "gui.securitycraft:scManual.customizableBlock": "Anpassbar: Bei diesem Block kannst du den Universellen Blockmodifizierer verwenden, um die Funktionen des Blockes anzupassen.", + "gui.securitycraft:scManual.customizableBlock": "Anpassbar: Du kannst den Universellen Blockmodifizierer verwenden, um die Funktionen hiervon anzupassen.", "gui.securitycraft:scManual.designedBy": "Entworfen von: %s", "gui.securitycraft:scManual.disabled": "Deaktiviert", "gui.securitycraft:scManual.display_cases": "Vitrinen", @@ -758,8 +758,8 @@ "gui.securitycraft:scManual.lockable": "Sperrbar: Dieser Block kann durch ein Schallsicherheitssystem gesperrt werden.", "gui.securitycraft:scManual.modules": "Verfügbare Module:", "gui.securitycraft:scManual.options": "Verfügbare Einstellungen:", - "gui.securitycraft:scManual.ownableBlock": "Besitzbar: Dieser Block ist resistent gegenüber Explosionen und kann nur vom Besitzer mit einem Universellen Blockentferner entfernt werden.", - "gui.securitycraft:scManual.passcodeProtectedBlock": "Passwortgesichert: Dieser Block kann nur benutzt werden, wenn das korrekte Passwort eingegeben wird.", + "gui.securitycraft:scManual.ownableBlock": "Besitzbar: Dies ist resistent gegenüber Explosionen und kann nur vom Besitzer mit einem Universellen Blockentferner entfernt werden.", + "gui.securitycraft:scManual.passcodeProtectedBlock": "Passwortgesichert: Dies kann nur benutzt werden, wenn das korrekte Passwort eingegeben wird.", "gui.securitycraft:scManual.patreon.error": "Fehler beim Laden der Patrons!", "gui.securitycraft:scManual.patreon.loading": "Lade...", "gui.securitycraft:scManual.patreon.title": "Unsere Patrons:", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index a172a0c292..3f1d1c1390 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -747,7 +747,7 @@ "gui.securitycraft:scManual.author": "Übersetzt von bl4ckscor3 und Redstone_Dubstep", "gui.securitycraft:scManual.block_mines": "Blockminen", "gui.securitycraft:scManual.block_reinforcers": "Universelle Blockverstärker", - "gui.securitycraft:scManual.customizableBlock": "Anpassbar: Bei diesem Block kannst du den Universellen Blockmodifizierer verwenden, um die Funktionen des Blockes anzupassen.", + "gui.securitycraft:scManual.customizableBlock": "Anpassbar: Du kannst den Universellen Blockmodifizierer verwenden, um die Funktionen hiervon anzupassen.", "gui.securitycraft:scManual.designedBy": "Entworfen von: %s", "gui.securitycraft:scManual.disabled": "Deaktiviert", "gui.securitycraft:scManual.display_cases": "Vitrinen", @@ -758,8 +758,8 @@ "gui.securitycraft:scManual.lockable": "Sperrbar: Dieser Block kann durch ein Schallsicherheitssystem gesperrt werden.", "gui.securitycraft:scManual.modules": "Verfügbare Module:", "gui.securitycraft:scManual.options": "Verfügbare Einstellungen:", - "gui.securitycraft:scManual.ownableBlock": "Besitzbar: Dieser Block ist resistent gegenüber Explosionen und kann nur vom Besitzer mit einem Universellen Blockentferner entfernt werden.", - "gui.securitycraft:scManual.passcodeProtectedBlock": "Passwortgesichert: Dieser Block kann nur benutzt werden, wenn das korrekte Passwort eingegeben wird.", + "gui.securitycraft:scManual.ownableBlock": "Besitzbar: Dies ist resistent gegenüber Explosionen und kann nur vom Besitzer mit einem Universellen Blockentferner entfernt werden.", + "gui.securitycraft:scManual.passcodeProtectedBlock": "Passwortgesichert: Dies kann nur benutzt werden, wenn das korrekte Passwort eingegeben wird.", "gui.securitycraft:scManual.patreon.error": "Fehler beim Laden der Patrons!", "gui.securitycraft:scManual.patreon.loading": "Lade...", "gui.securitycraft:scManual.patreon.title": "Unsere Patrons:", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index 641ac80986..4eda49afbb 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -754,7 +754,7 @@ "gui.securitycraft:rift_stabilizer.toggle": "Click the entries in the list below to toggle the detection of that specific teleportation type.", "gui.securitycraft:scManual.block_mines": "Block Mines", "gui.securitycraft:scManual.block_reinforcers": "Universal Block Reinforcers", - "gui.securitycraft:scManual.customizableBlock": "Customizable: You may use the Universal Block Modifier and the various modules added by SecurityCraft to customize the functions of this block.", + "gui.securitycraft:scManual.customizableBlock": "Customizable: You may use the Universal Block Modifier and the various modules added by SecurityCraft to customize the functions of this.", "gui.securitycraft:scManual.designedBy": "Designed by: %s", "gui.securitycraft:scManual.disabled": "Disabled", "gui.securitycraft:scManual.display_cases": "Display Cases", @@ -766,8 +766,8 @@ "gui.securitycraft:scManual.modules": "Available modules:", "gui.securitycraft:scManual.option_text": "- %s %s", "gui.securitycraft:scManual.options": "Available options:", - "gui.securitycraft:scManual.ownableBlock": "Ownable: This block is resistant to explosions, and can only be broken by the player who placed it down using the Universal Block Remover.", - "gui.securitycraft:scManual.passcodeProtectedBlock": "Passcode protected: This block can only be used if a passcode set by the owner is correctly entered.", + "gui.securitycraft:scManual.ownableBlock": "Ownable: This is resistant to explosions, and can only be broken by the player who placed it down using the Universal Block Remover.", + "gui.securitycraft:scManual.passcodeProtectedBlock": "Passcode protected: This can only be used if a passcode set by the owner is correctly entered.", "gui.securitycraft:scManual.patreon.error": "Error fetching Patrons!", "gui.securitycraft:scManual.patreon.loading": "Loading...", "gui.securitycraft:scManual.patreon.title": "Our Patrons:", From 26a9cf4a41629647e4a23e8daddb7f8f5eef9dfb Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 17:16:47 +0200 Subject: [PATCH 38/55] make the universal block remover work on boats --- .../net/geforcemods/securitycraft/entity/SecuritySeaBoat.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index b558a232c7..8f05d48511 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -147,6 +147,8 @@ public Component getDisplayName() { return InteractionResult.sidedSuccess(level.isClientSide); } + else if (stack.is(SCContent.UNIVERSAL_BLOCK_REMOVER.get()) && (isOwnedBy(player) || player.isCreative())) + destroy(damageSources().playerAttack(player)); } return super.interact(player, hand); From 32d7428063b378f99646b3b800c14198b4bfb195 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 19:09:33 +0200 Subject: [PATCH 39/55] fix a manual crash --- .../net/geforcemods/securitycraft/misc/SCManualPage.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java b/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java index 631b2980b4..b0b1ea6942 100644 --- a/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java +++ b/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java @@ -16,11 +16,12 @@ public Object getInWorldObject() { if (item instanceof BlockItem blockItem) { Block block = blockItem.getBlock(); - return ((EntityBlock) block).newBlockEntity(BlockPos.ZERO, block.defaultBlockState()); + if (block.defaultBlockState().hasBlockEntity()) + return ((EntityBlock) block).newBlockEntity(BlockPos.ZERO, block.defaultBlockState()); } else if (item instanceof BoatItem boatItem) return boatItem.getBoat(null, BlockHitResult.miss(Vec3.ZERO, Direction.NORTH, BlockPos.ZERO), item.getDefaultInstance(), null); - else - return null; + + return null; } } From 4b1564f37da83d90d8c46c65e9cc417869ca7a63 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 19:21:00 +0200 Subject: [PATCH 40/55] fix UBM not giving proper feedback sometimes --- .../securitycraft/entity/SecuritySeaBoat.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 8f05d48511..0a6b5c3318 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -125,25 +125,29 @@ else if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) return InteractionResult.sidedSuccess(level.isClientSide); } - else if (stack.is(SCContent.UNIVERSAL_BLOCK_MODIFIER.get()) && isOwnedBy(player)) { - if (!level.isClientSide) { - BlockPos pos = blockPosition(); - - player.openMenu(new MenuProvider() { - @Override - public AbstractContainerMenu createMenu(int windowId, Inventory inv, Player player) { - return new CustomizeBlockMenu(windowId, level, pos, SecuritySeaBoat.super.getId(), inv); - } - - @Override - public Component getDisplayName() { - return SecuritySeaBoat.super.getDisplayName(); - } - }, data -> { - data.writeBlockPos(pos); - data.writeVarInt(SecuritySeaBoat.super.getId()); - }); + else if (stack.is(SCContent.UNIVERSAL_BLOCK_MODIFIER.get())) { + if (isOwnedBy(player)) { + if (!level.isClientSide) { + BlockPos pos = blockPosition(); + + player.openMenu(new MenuProvider() { + @Override + public AbstractContainerMenu createMenu(int windowId, Inventory inv, Player player) { + return new CustomizeBlockMenu(windowId, level, pos, SecuritySeaBoat.super.getId(), inv); + } + + @Override + public Component getDisplayName() { + return SecuritySeaBoat.super.getDisplayName(); + } + }, data -> { + data.writeBlockPos(pos); + data.writeVarInt(SecuritySeaBoat.super.getId()); + }); + } } + else + PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED); return InteractionResult.sidedSuccess(level.isClientSide); } From 856527080853bf02c5cc434dd2d4d153d2ee243b Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 19:23:53 +0200 Subject: [PATCH 41/55] German translation fixes --- src/main/resources/assets/securitycraft/lang/de_at.json | 6 +++--- src/main/resources/assets/securitycraft/lang/de_ch.json | 6 +++--- src/main/resources/assets/securitycraft/lang/de_de.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index 3f1d1c1390..211c364784 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -897,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seeboot ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jeglichen Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -1106,7 +1106,7 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Ein Zulassungslisten-Modul in einem verstärkten Zauntor erlaubt es zugelassenen Spielern, dieses zu öffnen.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Ein Zulassungslisten-Modul in einer verstärkten Druckplatte erlaubt es zugelassenen Spielern, diese zu aktivieren.", "module.generic.secret_sign.whitelist_module.description": "Ein Zulassungslisten-Modul in einem geheimen Schild erlaubt es zugelassenen Spielern, dessen Text zu sehen und mit dem Schild zu interagieren.", - "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Schloss interagieren können.", + "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Boot interagieren können.", "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", @@ -1227,7 +1227,7 @@ "option.generic.disabled": "Deaktiviert: %s", "option.generic.disabled.description": "Soll dieser Block deaktiviert werden, sodass er bis zum Reaktivieren nicht mehr funktioniert?", "option.generic.ignoreOwner": "Besitzer ignorieren: %s", - "option.generic.ignoreOwner.description": "Soll dieser Block seinen Besitzer ignorieren? Dies führt dazu, dass jedwede Funktionalität des Blocks nicht für seinen Besitzer funktioniert.", + "option.generic.ignoreOwner.description": "Soll dieser Block seinen Besitzer ignorieren? Dies führt dazu, dass jegliche Funktionalität des Blocks nicht für seinen Besitzer funktioniert.", "option.generic.secret_sign.isBackSecret": "Geheime Rückseite: %s", "option.generic.secret_sign.isBackSecret.description": "Soll der Text auf der Rückseite des geheimen Schildes nur für den Besitzer und zugelassenen Spieler sichtbar sein?", "option.generic.secret_sign.isFrontSecret": "Geheime Vorderseite: %s", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index 3f1d1c1390..211c364784 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -897,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seeboot ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jeglichen Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -1106,7 +1106,7 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Ein Zulassungslisten-Modul in einem verstärkten Zauntor erlaubt es zugelassenen Spielern, dieses zu öffnen.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Ein Zulassungslisten-Modul in einer verstärkten Druckplatte erlaubt es zugelassenen Spielern, diese zu aktivieren.", "module.generic.secret_sign.whitelist_module.description": "Ein Zulassungslisten-Modul in einem geheimen Schild erlaubt es zugelassenen Spielern, dessen Text zu sehen und mit dem Schild zu interagieren.", - "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Schloss interagieren können.", + "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Boot interagieren können.", "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", @@ -1227,7 +1227,7 @@ "option.generic.disabled": "Deaktiviert: %s", "option.generic.disabled.description": "Soll dieser Block deaktiviert werden, sodass er bis zum Reaktivieren nicht mehr funktioniert?", "option.generic.ignoreOwner": "Besitzer ignorieren: %s", - "option.generic.ignoreOwner.description": "Soll dieser Block seinen Besitzer ignorieren? Dies führt dazu, dass jedwede Funktionalität des Blocks nicht für seinen Besitzer funktioniert.", + "option.generic.ignoreOwner.description": "Soll dieser Block seinen Besitzer ignorieren? Dies führt dazu, dass jegliche Funktionalität des Blocks nicht für seinen Besitzer funktioniert.", "option.generic.secret_sign.isBackSecret": "Geheime Rückseite: %s", "option.generic.secret_sign.isBackSecret.description": "Soll der Text auf der Rückseite des geheimen Schildes nur für den Besitzer und zugelassenen Spieler sichtbar sein?", "option.generic.secret_sign.isFrontSecret": "Geheime Vorderseite: %s", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index 3f1d1c1390..211c364784 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -897,7 +897,7 @@ "help.securitycraft.scanner_trapdoor.info": "Die Scannerfalltür verhält sich wie der Augenscanner. Sie öffnet sich von selbst, wenn sie anschaut.", "help.securitycraft.secret_signs.info": "Das Geheime Schild funktioniert wie das Vanilla-Schild, mit dem Unterschied, dass nur der Besitzer und zugelassene Spieler den Text lesen und mit dem Schild interagieren können. Stelle sicher, dass keiner den Block, an dem das Schild ist, zerstören kann!", "help.securitycraft.security_camera.info": "Die Überwachungskamera erlaubt dir, eine Region zu überwachen, in der du nicht bist. Platziere die Kamera und rechtsklicke sie mit einem Kameramonitor (Item), um sie mit dem Monitor zu verbinden.", - "help.securitycraft.security_sea_boats.info": "Das gesicherte Seebote ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jedweden Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", + "help.securitycraft.security_sea_boats.info": "Das gesicherte Seeboot ist ein Truhenboot mit einer Passwortgesicherten Truhe. Jeder, der das richtige Passwort hat, kann die Truhe öffnen, aber nur der Besitzer kann das Boot zerbrechen. Das Boot ist immun gegen jeglichen Schaden, inklusive Lava. Es kann zwar auf Lava schwimmen, jedoch ist es sehr langsam und der Passagier nimmt Schaden.", "help.securitycraft.sentry.info": "Das Geschütz attackiert Eindringlinge mit einer unlimitierten Anzahl von Geschützkugeln, oder mit anderen Projektilen, die verbraucht werden, wenn sie in eine Passwortgesicherte Truhe oder Fass unter dem Geschütz gelegt werden. Es hat drei Modi und drei Zieloptionen, die mit einem Rechtsklick auf das Geschütz gewechselt werden können. Die drei Modi sind \"Untätig\" (es wird niemals angreifen), \"Tarnen\" (es wird nur aktiviert, sobald ein Ziel in Reichweite ist) und \"Aggressiv\" (es wird immer aktiv sein). Die beiden letztgenannten Modi sind mit den Zieloptionen \"Nur Monster\", \"Monster und Spieler\", und \"Nur Spieler\" verfügbar. Ein Tarnmodul sorgt dafür, dass sich das Geschütz mit dem im Modul hinzugefügten Block tarnt, ein Schnelligkeits-Modul erhöht die Angriffsgeschwindigkeit des Geschützes, und ein Zulassungslisten-Modul verhindert, dass es auf zugelassene Spieler schießt. Die Module können mit einem Rechtsklick auf das Geschütz eingefügt oder ausgewechselt werden, und mithilfe eines Universellen Blockmodifizierers entfernt werden. Um das Geschütz zu entfernen, schleich-rechtsklicke es.", "help.securitycraft.smart_module.info": "Das schlaue Modul berücksichtigt auch Verzauberungs- und NBT-Daten eines Items z.B. im Inventarscanner.", "help.securitycraft.sonic_security_system.info": "Das Schallsicherheitssystem, inspiriert vom \"Sonic Security System\" im Spiel \"Metroid Prime 2: Echoes\", kann bestimmte SecurityCraft Blöcke sperren und somit Spieler daran hindern, sie zu nutzen. Rechtsklicke einen solchen Block, um ihn mit dem Schallsicherheitssystem zu verbinden. Wenn das Schallsicherheitssystem platziert ist, kann via Rechtsklick ein GUI geöffnet werden, das ermöglicht, es zu aktivieren, deaktivieren, oder zurückzusetzen. Die Blöcke lassen sich kurzzeitig entsperren, indem eine bestimmte Melodie - entweder mit einem tragbaren Melodienspieler oder Notenblöcken in der Nähe - abgespielt wird. Um die Melodie zu bestimmen, starte die Aufnahme im GUI des Schallsicherheitssystems, und spiele eine Tonfolge in der Nähe. Sobald du fertig bist, stoppe die Aufnahme. Nun werden die durch dieses Schallsicherheitssystem gesperrten Blöcke jedes Mal, wenn diese Tonfolge in der Nähe des Schallsicherheitssystems gespielt wird, kurzzeitig entsperrt.", @@ -1106,7 +1106,7 @@ "module.generic.reinforced_fence_gate.whitelist_module.description": "Ein Zulassungslisten-Modul in einem verstärkten Zauntor erlaubt es zugelassenen Spielern, dieses zu öffnen.", "module.generic.reinforced_pressure_plate.whitelist_module.description": "Ein Zulassungslisten-Modul in einer verstärkten Druckplatte erlaubt es zugelassenen Spielern, diese zu aktivieren.", "module.generic.secret_sign.whitelist_module.description": "Ein Zulassungslisten-Modul in einem geheimen Schild erlaubt es zugelassenen Spielern, dessen Text zu sehen und mit dem Schild zu interagieren.", - "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Schloss interagieren können.", + "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Boot interagieren können.", "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", @@ -1227,7 +1227,7 @@ "option.generic.disabled": "Deaktiviert: %s", "option.generic.disabled.description": "Soll dieser Block deaktiviert werden, sodass er bis zum Reaktivieren nicht mehr funktioniert?", "option.generic.ignoreOwner": "Besitzer ignorieren: %s", - "option.generic.ignoreOwner.description": "Soll dieser Block seinen Besitzer ignorieren? Dies führt dazu, dass jedwede Funktionalität des Blocks nicht für seinen Besitzer funktioniert.", + "option.generic.ignoreOwner.description": "Soll dieser Block seinen Besitzer ignorieren? Dies führt dazu, dass jegliche Funktionalität des Blocks nicht für seinen Besitzer funktioniert.", "option.generic.secret_sign.isBackSecret": "Geheime Rückseite: %s", "option.generic.secret_sign.isBackSecret.description": "Soll der Text auf der Rückseite des geheimen Schildes nur für den Besitzer und zugelassenen Spieler sichtbar sein?", "option.generic.secret_sign.isFrontSecret": "Geheime Vorderseite: %s", From a7c03e29432f8cca55dedd81731060945bacc717 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 19:31:10 +0200 Subject: [PATCH 42/55] respect list modules when interacting with the boat --- .../securitycraft/entity/SecuritySeaBoat.java | 12 ++++++++++++ .../resources/assets/securitycraft/lang/de_at.json | 2 +- .../resources/assets/securitycraft/lang/de_ch.json | 2 +- .../resources/assets/securitycraft/lang/de_de.json | 2 +- .../resources/assets/securitycraft/lang/en_us.json | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 0a6b5c3318..7119b41949 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -93,8 +93,20 @@ protected void defineSynchedData() { entityData.define(MODULE_STATES, new EnumMap<>(ModuleType.class)); } + @Override + public boolean canAddPassenger(Entity passenger) { + return super.canAddPassenger(passenger) && (isOwnedBy(passenger) || isAllowed(passenger)) && !isDenied(passenger); + } + @Override public InteractionResult interact(Player player, InteractionHand hand) { + if (isDenied(player)) { + if (sendsDenylistMessage()) + PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:module.onDenylist"), ChatFormatting.RED); + + return InteractionResult.FAIL; + } + if (player.isSecondaryUseActive()) { ItemStack stack = player.getItemInHand(hand); Level level = player.level(); diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index 211c364784..deb681bc02 100644 --- a/src/main/resources/assets/securitycraft/lang/de_at.json +++ b/src/main/resources/assets/securitycraft/lang/de_at.json @@ -1109,7 +1109,7 @@ "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Boot interagieren können.", "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", - "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", + "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern in das Boot einzusteigen, sowie die Truhe zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", "module.securitycraft.alarm.smart_module.description": "Ein schlaues Modul in einem Alarm lässt dich einstellen, welches Geräusch der Alarm abspielt, wenn er aktiviert wird.", "module.securitycraft.block_change_detector.disguise_module.description": "Wenn ein Tarnmodul zu einem Block-Änderungsdetektor hinzugefügt wird, ändert es seine Textur/sein Modell zu denen des Blockes im Tarnmodul.", "module.securitycraft.block_change_detector.redstone_module.description": "Ein Redstone-Modul in einem Block-Änderungsdetektor lässt ihn ein Redstonesignal abgeben, wenn ein Spieler einen Block in seiner Nähte platziert/zerbricht.", diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json index 211c364784..deb681bc02 100644 --- a/src/main/resources/assets/securitycraft/lang/de_ch.json +++ b/src/main/resources/assets/securitycraft/lang/de_ch.json @@ -1109,7 +1109,7 @@ "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Boot interagieren können.", "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", - "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", + "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern in das Boot einzusteigen, sowie die Truhe zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", "module.securitycraft.alarm.smart_module.description": "Ein schlaues Modul in einem Alarm lässt dich einstellen, welches Geräusch der Alarm abspielt, wenn er aktiviert wird.", "module.securitycraft.block_change_detector.disguise_module.description": "Wenn ein Tarnmodul zu einem Block-Änderungsdetektor hinzugefügt wird, ändert es seine Textur/sein Modell zu denen des Blockes im Tarnmodul.", "module.securitycraft.block_change_detector.redstone_module.description": "Ein Redstone-Modul in einem Block-Änderungsdetektor lässt ihn ein Redstonesignal abgeben, wenn ein Spieler einen Block in seiner Nähte platziert/zerbricht.", diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json index 211c364784..deb681bc02 100644 --- a/src/main/resources/assets/securitycraft/lang/de_de.json +++ b/src/main/resources/assets/securitycraft/lang/de_de.json @@ -1109,7 +1109,7 @@ "module.generic.security_sea_boat.blacklist_module.description": "Ein Sperrlisten-Modul in einem gesicherten Seeboot verhindert, dass gesperrte Spieler mit dem Boot interagieren können.", "module.generic.security_sea_boat.harming_module.description": "Wenn ein Schadensmodul zu einem gesicherten Seeboot hinzugefügt wird, bekommen Spieler Schaden, wenn sie einen falschen Code eingeben.", "module.generic.security_sea_boat.smart_module.description": "Wenn ein schlaues Modul zu einem gesicherten Seeboot hinzugefügt wird, existiert eine Abklingzeit, nachdem ein inkorrekter Code eingegeben wurde. In dieser Zeit kann kein neuer Code eingegeben werden.", - "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern, es zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", + "module.generic.security_sea_boat.whitelist_module.description": "Ein Zulassungslisten-Modul in einem gesicherten Seeboot erlaubt zugelassenen Spielern in das Boot einzusteigen, sowie die Truhe zu öffnen, ohne den Code zu kennen. Zusätzlich kann ein verstärkter Trichter eines zugelassenen Spielers Items aus der Truhe ziehen.", "module.securitycraft.alarm.smart_module.description": "Ein schlaues Modul in einem Alarm lässt dich einstellen, welches Geräusch der Alarm abspielt, wenn er aktiviert wird.", "module.securitycraft.block_change_detector.disguise_module.description": "Wenn ein Tarnmodul zu einem Block-Änderungsdetektor hinzugefügt wird, ändert es seine Textur/sein Modell zu denen des Blockes im Tarnmodul.", "module.securitycraft.block_change_detector.redstone_module.description": "Ein Redstone-Modul in einem Block-Änderungsdetektor lässt ihn ein Redstonesignal abgeben, wenn ein Spieler einen Block in seiner Nähte platziert/zerbricht.", diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json index 4eda49afbb..e3788c27e9 100644 --- a/src/main/resources/assets/securitycraft/lang/en_us.json +++ b/src/main/resources/assets/securitycraft/lang/en_us.json @@ -1118,7 +1118,7 @@ "module.generic.security_sea_boat.blacklist_module.description": "Adding a denylist module to a Security Sea Boat will ban listed players from interacting with the boat.", "module.generic.security_sea_boat.harming_module.description": "Adding a harming module to a Security Sea Boat will result in the player getting damaged, if they enter an incorrect code.", "module.generic.security_sea_boat.smart_module.description": "Adding a smart module to a Security Sea Boat will result in a cooldown being applied after an incorrect code has been entered. During that time, no new code can be entered.", - "module.generic.security_sea_boat.whitelist_module.description": "Adding an allowlist module to a Security Sea Boat will allow players to open it without knowing the code. Additionally, Reinforced Hoppers owned by listed players can extract items from the chest.", + "module.generic.security_sea_boat.whitelist_module.description": "Adding an allowlist module to a Security Sea Boat will allow players to get into the boat, as well as open the chest without knowing the code. Additionally, Reinforced Hoppers owned by listed players can extract items from the chest.", "module.securitycraft.alarm.smart_module.description": "Adding a smart module to an alarm will allow you to select which sound the alarm plays when it is activated.", "module.securitycraft.block_change_detector.disguise_module.description": "Adding a disguise module to a block change detector will cause the texture & model of it to change to the texture & model of the block that is added to the disguise module.", "module.securitycraft.block_change_detector.redstone_module.description": "Adding a redstone module to a block change detector makes it emit a redstone signal each time it detects a block was changed by a player in its vicinity.", From f65bdb34966e86bd955bdd796b6b30f7bd539551 Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 19:33:01 +0200 Subject: [PATCH 43/55] drop modules when boat's owner is changed --- .../geforcemods/securitycraft/entity/SecuritySeaBoat.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 7119b41949..0388c839a7 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -52,6 +52,7 @@ import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.Vec3; +import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.common.NeoForgeMod; import net.neoforged.neoforge.fluids.FluidType; import net.neoforged.neoforge.network.PacketDistributor; @@ -131,6 +132,10 @@ else if (stack.is(SCContent.UNIVERSAL_OWNER_CHANGER.get()) && isOwnedBy(player)) if (!level.isClientSide) { String newOwner = stack.getHoverName().getString(); + //disable this in a development environment + if (FMLEnvironment.production) + dropAllModules(); + setOwner(PlayerUtils.isPlayerOnline(newOwner) ? PlayerUtils.getPlayerFromName(newOwner).getUUID().toString() : "ownerUUID", newOwner); PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_OWNER_CHANGER.get().getDescriptionId()), Utils.localize("messages.securitycraft:universalOwnerChanger.changed", newOwner), ChatFormatting.GREEN); } From 9352029fc1063677bb507aef285c674a5d5e1d7c Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 19:49:40 +0200 Subject: [PATCH 44/55] hurt passenger when in lava like fire does --- .../securitycraft/entity/SecuritySeaBoat.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java index 0388c839a7..21986aa99d 100644 --- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -245,11 +245,15 @@ public void tick() { if (!level().isClientSide && isInLava) { Entity passenger = getFirstPassenger(); - if (passenger != null && !passenger.fireImmune()) { - passenger.setRemainingFireTicks(passenger.getRemainingFireTicks() + 1); + if (passenger != null) { + if (!passenger.fireImmune()) { + passenger.setRemainingFireTicks(passenger.getRemainingFireTicks() + 1); - if (passenger.getRemainingFireTicks() == 0) - passenger.setSecondsOnFire(8); + if (passenger.getRemainingFireTicks() == 0) + passenger.setSecondsOnFire(8); + } + + passenger.hurt(level().damageSources().inFire(), 1.0F); } } } From 41fe25186175a7fe870392102b3c021f771e739f Mon Sep 17 00:00:00 2001 From: bl4ckscor3 Date: Mon, 20 May 2024 20:14:11 +0200 Subject: [PATCH 45/55] fix vanilla hoppers and hoppers on non-allowed players extracting items --- .../securitycraft/RegistrationHandler.java | 5 ++++- .../securitycraft/api/IExtractionBlock.java | 14 ++++++------- .../blockentities/KeypadChestBlockEntity.java | 2 +- .../ReinforcedHopperBlockEntity.java | 2 +- .../securitycraft/blocks/mines/IMSBlock.java | 4 ++-- .../reinforced/ReinforcedHopperBlock.java | 6 +++--- .../securitycraft/entity/SecuritySeaBoat.java | 9 +++++++++ .../securitycraft/util/BlockUtils.java | 20 +++++++++---------- 8 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java b/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java index 0399d8381f..93ae4a1039 100644 --- a/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java +++ b/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java @@ -16,6 +16,7 @@ import net.geforcemods.securitycraft.blockentities.ReinforcedHopperBlockEntity; import net.geforcemods.securitycraft.blockentities.SecurityCameraBlockEntity; import net.geforcemods.securitycraft.blockentities.TrophySystemBlockEntity; +import net.geforcemods.securitycraft.entity.SecuritySeaBoat; import net.geforcemods.securitycraft.misc.SCSounds; import net.geforcemods.securitycraft.network.client.BlockPocketManagerFailedActivation; import net.geforcemods.securitycraft.network.client.OpenScreen; @@ -216,13 +217,15 @@ public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.CLAYMORE_BLOCK_ENTITY.get(), ClaymoreBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.INVENTORY_SCANNER_BLOCK_ENTITY.get(), InventoryScannerBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.KEYPAD_BARREL_BLOCK_ENTITY.get(), KeypadBarrelBlockEntity::getCapability); - event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.KEYPAD_CHEST_BLOCK_ENTITY.get(), KeypadChestBlockEntity::getCapability); + event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.KEYPAD_CHEST_BLOCK_ENTITY.get(), (chest, dir) -> KeypadChestBlockEntity.getCapability((KeypadChestBlockEntity) chest, dir)); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.LASER_BLOCK_BLOCK_ENTITY.get(), LaserBlockBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.REINFORCED_HOPPER_BLOCK_ENTITY.get(), ReinforcedHopperBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.TROPHY_SYSTEM_BLOCK_ENTITY.get(), TrophySystemBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.REINFORCED_DISPENSER_BLOCK_ENTITY.get(), ReinforcedDispenserBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.REINFORCED_DROPPER_BLOCK_ENTITY.get(), ReinforcedDropperBlockEntity::getCapability); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, SCContent.SECURITY_CAMERA_BLOCK_ENTITY.get(), SecurityCameraBlockEntity::getCapability); + event.registerEntity(Capabilities.ItemHandler.ENTITY, SCContent.SECURITY_SEA_BOAT_ENTITY.get(), (boat, void_) -> SecuritySeaBoat.getCapability(boat, null)); + event.registerEntity(Capabilities.ItemHandler.ENTITY_AUTOMATION, SCContent.SECURITY_SEA_BOAT_ENTITY.get(), SecuritySeaBoat::getCapability); } @SubscribeEvent diff --git a/src/main/java/net/geforcemods/securitycraft/api/IExtractionBlock.java b/src/main/java/net/geforcemods/securitycraft/api/IExtractionBlock.java index 0316584085..ceee478904 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/IExtractionBlock.java +++ b/src/main/java/net/geforcemods/securitycraft/api/IExtractionBlock.java @@ -6,8 +6,8 @@ import net.minecraft.world.level.block.state.BlockState; /** - * Defines a block that can extract from a Passcode-protected Chest, Passcode-protected Furnace, and Block Pocket Manager. - * Call + * Defines a block that can extract from a Passcode-protected Chest, Passcode-protected Furnace, Block Pocket Manager, + * Security Sea Boat, ...

Call * *

  * InterModComms.sendTo("securitycraft", SecurityCraftAPI.IMC_EXTRACTION_BLOCK_MSG, ClassThatImplementsIExtractionBlock::new);
@@ -19,18 +19,18 @@
  */
 public interface IExtractionBlock {
 	/**
-	 * The protected block uses this to check if this block can extract items
+	 * The protected object uses this to check if this block can extract items
 	 *
-	 * @param be The block entity of the protected block
-	 * @param level The level that the protected block is in
+	 * @param ownable The protected object
+	 * @param level The level that the object is in
 	 * @param pos The position of the block that is trying to extract items
 	 * @param state The state of the block that is trying to extract items
 	 * @return true if extraction is possible, false otherwise
 	 */
-	public boolean canExtract(IOwnable be, Level level, BlockPos pos, BlockState state);
+	public boolean canExtract(IOwnable ownable, Level level, BlockPos pos, BlockState state);
 
 	/**
-	 * @return The block that is trying to extract from a passcode-protected chest/furnace
+	 * @return The block that is trying to extract from something
 	 */
 	public Block getBlock();
 }
diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java
index 5a3522c943..bcf4b7b64c 100644
--- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java
+++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java
@@ -141,7 +141,7 @@ public int getNumPlayersUsing() {
 		return openersCounter.getOpenerCount();
 	}
 
-	public static IItemHandler getCapability(ChestBlockEntity be, Direction side) {
+	public static IItemHandler getCapability(KeypadChestBlockEntity be, Direction side) {
 		if (BlockUtils.isAllowedToExtractFromProtectedBlock(side, be))
 			return new InvWrapper(ChestBlock.getContainer((ChestBlock) be.getBlockState().getBlock(), be.getBlockState(), be.getLevel(), be.getBlockPos(), true));
 		else
diff --git a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java
index 8ba21d50ef..d1e745c766 100644
--- a/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java
+++ b/src/main/java/net/geforcemods/securitycraft/blockentities/ReinforcedHopperBlockEntity.java
@@ -111,7 +111,7 @@ protected Component getDefaultName() {
 		return Component.translatable(SCContent.REINFORCED_HOPPER.get().getDescriptionId());
 	}
 
-	public static IItemHandler getCapability(HopperBlockEntity be, Direction side) {
+	public static IItemHandler getCapability(ReinforcedHopperBlockEntity be, Direction side) {
 		return BlockUtils.isAllowedToExtractFromProtectedBlock(side, be) ? new VanillaHopperItemHandler(be) : new VanillaHopperInsertOnlyItemHandler(be);
 	}
 
diff --git a/src/main/java/net/geforcemods/securitycraft/blocks/mines/IMSBlock.java b/src/main/java/net/geforcemods/securitycraft/blocks/mines/IMSBlock.java
index 0e9f0fffa0..470257d721 100644
--- a/src/main/java/net/geforcemods/securitycraft/blocks/mines/IMSBlock.java
+++ b/src/main/java/net/geforcemods/securitycraft/blocks/mines/IMSBlock.java
@@ -169,8 +169,8 @@ public  BlockEntityTicker getTicker(Level level, Block
 
 	public static class ExtractionBlock implements IExtractionBlock {
 		@Override
-		public boolean canExtract(IOwnable be, Level level, BlockPos pos, BlockState state) {
-			return be.getOwner().owns((IMSBlockEntity) level.getBlockEntity(pos));
+		public boolean canExtract(IOwnable ownable, Level level, BlockPos pos, BlockState state) {
+			return ownable.getOwner().owns((IMSBlockEntity) level.getBlockEntity(pos));
 		}
 
 		@Override
diff --git a/src/main/java/net/geforcemods/securitycraft/blocks/reinforced/ReinforcedHopperBlock.java b/src/main/java/net/geforcemods/securitycraft/blocks/reinforced/ReinforcedHopperBlock.java
index f420a2b1db..34c38c29f7 100644
--- a/src/main/java/net/geforcemods/securitycraft/blocks/reinforced/ReinforcedHopperBlock.java
+++ b/src/main/java/net/geforcemods/securitycraft/blocks/reinforced/ReinforcedHopperBlock.java
@@ -84,13 +84,13 @@ public Block getVanillaBlock() {
 
 	public static class ExtractionBlock implements IExtractionBlock {
 		@Override
-		public boolean canExtract(IOwnable be, Level level, BlockPos pos, BlockState state) {
+		public boolean canExtract(IOwnable ownable, Level level, BlockPos pos, BlockState state) {
 			ReinforcedHopperBlockEntity hopperBe = (ReinforcedHopperBlockEntity) level.getBlockEntity(pos);
 
 			if (!hopperBe.getOwner().isValidated())
 				return false;
-			else if (!be.getOwner().owns(hopperBe)) {
-				if (be instanceof IModuleInventory inv)
+			else if (!ownable.getOwner().owns(hopperBe)) {
+				if (ownable instanceof IModuleInventory inv)
 					return inv.isAllowed(hopperBe.getOwner().getName()); //hoppers can extract out of e.g. chests if the hopper's owner is on the chest's allowlist module
 
 				return false;
diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index 21986aa99d..41d4c418ee 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -17,15 +17,18 @@
 import net.geforcemods.securitycraft.api.Option.SmartModuleCooldownOption;
 import net.geforcemods.securitycraft.api.Owner;
 import net.geforcemods.securitycraft.inventory.CustomizeBlockMenu;
+import net.geforcemods.securitycraft.inventory.InsertOnlyInvWrapper;
 import net.geforcemods.securitycraft.misc.ModuleType;
 import net.geforcemods.securitycraft.misc.SaltData;
 import net.geforcemods.securitycraft.network.client.OpenScreen;
 import net.geforcemods.securitycraft.network.client.OpenScreen.DataType;
+import net.geforcemods.securitycraft.util.BlockUtils;
 import net.geforcemods.securitycraft.util.PasscodeUtils;
 import net.geforcemods.securitycraft.util.PlayerUtils;
 import net.geforcemods.securitycraft.util.Utils;
 import net.minecraft.ChatFormatting;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
 import net.minecraft.core.NonNullList;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.chat.Component;
@@ -55,6 +58,8 @@
 import net.neoforged.fml.loading.FMLEnvironment;
 import net.neoforged.neoforge.common.NeoForgeMod;
 import net.neoforged.neoforge.fluids.FluidType;
+import net.neoforged.neoforge.items.IItemHandler;
+import net.neoforged.neoforge.items.wrapper.InvWrapper;
 import net.neoforged.neoforge.network.PacketDistributor;
 
 public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodeProtected, IModuleInventory, ICustomizable {
@@ -302,6 +307,10 @@ public void remove(RemovalReason reason) {
 		super.remove(reason);
 	}
 
+	public static IItemHandler getCapability(SecuritySeaBoat boat, Direction direction) {
+		return BlockUtils.isAllowedToExtractFromProtectedBlock(direction, boat, boat.level(), boat.blockPosition()) ? new InvWrapper(boat) : new InsertOnlyInvWrapper(boat);
+	}
+
 	@Override
 	protected void addAdditionalSaveData(CompoundTag tag) {
 		CompoundTag ownerTag = new CompoundTag();
diff --git a/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java b/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java
index 3c8296272a..e05e8e248e 100644
--- a/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java
+++ b/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java
@@ -74,18 +74,18 @@ private static boolean hasActiveSCBlockNextTo(Level level, BlockPos pos, BlockEn
 		return false;
 	}
 
-	public static boolean isAllowedToExtractFromProtectedBlock(Direction side, BlockEntity be) {
-		if (side != null) {
-			Level level = be.getLevel();
+	public static  boolean isAllowedToExtractFromProtectedBlock(Direction side, T be) {
+		return isAllowedToExtractFromProtectedBlock(side, be, be.getLevel(), be.getBlockPos());
+	}
 
-			if (level != null) {
-				BlockPos offsetPos = be.getBlockPos().relative(side);
-				BlockState offsetState = level.getBlockState(offsetPos);
+	public static boolean isAllowedToExtractFromProtectedBlock(Direction side, IOwnable be, Level level, BlockPos pos) {
+		if (side != null && level != null) {
+			BlockPos offsetPos = pos.relative(side);
+			BlockState offsetState = level.getBlockState(offsetPos);
 
-				for (IExtractionBlock extractionBlock : SecurityCraftAPI.getRegisteredExtractionBlocks()) {
-					if (offsetState.getBlock() == extractionBlock.getBlock())
-						return extractionBlock.canExtract((IOwnable) be, level, offsetPos, offsetState);
-				}
+			for (IExtractionBlock extractionBlock : SecurityCraftAPI.getRegisteredExtractionBlocks()) {
+				if (offsetState.getBlock() == extractionBlock.getBlock())
+					return extractionBlock.canExtract(be, level, offsetPos, offsetState);
 			}
 		}
 

From 0576123fb75b1b6720685d059d2c4a9171d3986d Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Mon, 20 May 2024 20:15:45 +0200
Subject: [PATCH 46/55] fix UBR not giving proper feedback sometimes

---
 .../geforcemods/securitycraft/entity/SecuritySeaBoat.java | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index 41d4c418ee..2277414e4a 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -173,8 +173,12 @@ public Component getDisplayName() {
 
 				return InteractionResult.sidedSuccess(level.isClientSide);
 			}
-			else if (stack.is(SCContent.UNIVERSAL_BLOCK_REMOVER.get()) && (isOwnedBy(player) || player.isCreative()))
-				destroy(damageSources().playerAttack(player));
+			else if (stack.is(SCContent.UNIVERSAL_BLOCK_REMOVER.get())) {
+				if (isOwnedBy(player) || player.isCreative())
+					destroy(damageSources().playerAttack(player));
+				else
+					PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_BLOCK_REMOVER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED);
+			}
 		}
 
 		return super.interact(player, hand);

From 13d75ae99829a4f01c5358c4b5cac7d9d621035d Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Mon, 20 May 2024 20:49:10 +0200
Subject: [PATCH 47/55] fix codebreaker not being usable when on the denylist

also fix a duplicate message
---
 .../securitycraft/entity/SecuritySeaBoat.java      | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index 2277414e4a..9016a209a6 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -106,24 +106,24 @@ public boolean canAddPassenger(Entity passenger) {
 
 	@Override
 	public InteractionResult interact(Player player, InteractionHand hand) {
-		if (isDenied(player)) {
+		ItemStack stack = player.getItemInHand(hand);
+		Level level = player.level();
+
+		if (isDenied(player) && !(player.isSecondaryUseActive() && stack.is(SCContent.CODEBREAKER.get()))) {
 			if (sendsDenylistMessage())
 				PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:module.onDenylist"), ChatFormatting.RED);
 
-			return InteractionResult.FAIL;
+			return InteractionResult.sidedSuccess(level.isClientSide);
 		}
 
 		if (player.isSecondaryUseActive()) {
-			ItemStack stack = player.getItemInHand(hand);
-			Level level = player.level();
-
-			if (player.isHolding(SCContent.CODEBREAKER.get())) {
+			if (stack.is(SCContent.CODEBREAKER.get())) {
 				if (!level.isClientSide)
 					handleCodebreaking(player, player.getMainHandItem().is(SCContent.CODEBREAKER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND);
 
 				return InteractionResult.sidedSuccess(level.isClientSide);
 			}
-			else if (player.isHolding(SCContent.UNIVERSAL_KEY_CHANGER.get())) {
+			else if (stack.is(SCContent.UNIVERSAL_KEY_CHANGER.get())) {
 				if (!level.isClientSide) {
 					if (isOwnedBy(player) || player.isCreative())
 						PacketDistributor.PLAYER.with((ServerPlayer) player).send(new OpenScreen(DataType.CHANGE_PASSCODE_FOR_ENTITY, getId()));

From 3238844463ff47faa92979b43ab9b1156ff0ad5a Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Mon, 20 May 2024 20:54:43 +0200
Subject: [PATCH 48/55] add hud mod support

---
 .../compat/hudmods/HudModHandler.java           | 17 ++++++++++++-----
 .../compat/hudmods/JadeDataProvider.java        |  4 +++-
 .../compat/hudmods/TOPDataProvider.java         |  2 +-
 .../compat/hudmods/WTHITDataProvider.java       |  4 +++-
 4 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/HudModHandler.java b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/HudModHandler.java
index 835d9642a2..6e836d7689 100644
--- a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/HudModHandler.java
+++ b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/HudModHandler.java
@@ -9,6 +9,7 @@
 import net.geforcemods.securitycraft.api.IOwnable;
 import net.geforcemods.securitycraft.blocks.DisguisableBlock;
 import net.geforcemods.securitycraft.compat.IOverlayDisplay;
+import net.geforcemods.securitycraft.entity.SecuritySeaBoat;
 import net.geforcemods.securitycraft.entity.sentry.Sentry;
 import net.geforcemods.securitycraft.entity.sentry.Sentry.SentryMode;
 import net.geforcemods.securitycraft.misc.ModuleType;
@@ -41,7 +42,7 @@ public class HudModHandler {
 
 	protected HudModHandler() {}
 
-	public void addOwnerModuleNameInfo(Level level, BlockPos pos, BlockState state, Block block, BlockEntity be, Player player, Consumer lineAdder, Predicate configGetter) {
+	public void addDisguisedOwnerModuleNameInfo(Level level, BlockPos pos, BlockState state, Block block, BlockEntity be, Player player, Consumer lineAdder, Predicate configGetter) {
 		boolean disguised = false;
 
 		if (block instanceof DisguisableBlock) {
@@ -56,11 +57,15 @@ public void addOwnerModuleNameInfo(Level level, BlockPos pos, BlockState state,
 		if (be == null || disguised || block instanceof IOverlayDisplay display && !display.shouldShowSCInfo(level, state, pos))
 			return;
 
-		if (configGetter.test(SHOW_OWNER) && be instanceof IOwnable ownable)
+		addOwnerModuleNameInfo(be, player, lineAdder, configGetter);
+	}
+
+	public void addOwnerModuleNameInfo(Object obj, Player player, Consumer lineAdder, Predicate configGetter) {
+		if (configGetter.test(SHOW_OWNER) && obj instanceof IOwnable ownable)
 			lineAdder.accept(Utils.localize("waila.securitycraft:owner", PlayerUtils.getOwnerComponent(ownable.getOwner())).withStyle(ChatFormatting.GRAY));
 
-		//if the te is ownable, show modules only when it's owned, otherwise always show
-		if (configGetter.test(SHOW_MODULES) && be instanceof IModuleInventory inv && !inv.getInsertedModules().isEmpty() && (!(be instanceof IOwnable ownable) || ownable.isOwnedBy(player))) {
+		//if the object is ownable, show modules only when it's owned, otherwise always show
+		if (configGetter.test(SHOW_MODULES) && obj instanceof IModuleInventory inv && !inv.getInsertedModules().isEmpty() && (!(obj instanceof IOwnable ownable) || ownable.isOwnedBy(player))) {
 			lineAdder.accept(EQUIPPED);
 
 			for (ModuleType module : inv.getInsertedModules()) {
@@ -68,7 +73,7 @@ public void addOwnerModuleNameInfo(Level level, BlockPos pos, BlockState state,
 			}
 		}
 
-		if (configGetter.test(SHOW_CUSTOM_NAME) && be instanceof Nameable nameable && nameable.hasCustomName()) {
+		if (configGetter.test(SHOW_CUSTOM_NAME) && obj instanceof Nameable nameable && nameable.hasCustomName()) {
 			Component text = nameable.getCustomName();
 			Component name = text == null ? Component.empty() : text;
 
@@ -103,5 +108,7 @@ public void addEntityInfo(Entity entity, Player player, Consumer line
 
 			lineAdder.accept(modeDescription.withStyle(ChatFormatting.GRAY));
 		}
+		else if (entity instanceof SecuritySeaBoat boat)
+			addOwnerModuleNameInfo(boat, player, lineAdder, configGetter);
 	}
 }
diff --git a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/JadeDataProvider.java b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/JadeDataProvider.java
index 6a9909c0eb..7536998961 100644
--- a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/JadeDataProvider.java
+++ b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/JadeDataProvider.java
@@ -5,6 +5,7 @@
 import net.geforcemods.securitycraft.blocks.FakeLavaBlock;
 import net.geforcemods.securitycraft.blocks.FakeWaterBlock;
 import net.geforcemods.securitycraft.compat.IOverlayDisplay;
+import net.geforcemods.securitycraft.entity.SecuritySeaBoat;
 import net.geforcemods.securitycraft.entity.sentry.Sentry;
 import net.minecraft.core.BlockPos;
 import net.minecraft.network.chat.Component;
@@ -36,6 +37,7 @@ public void registerClient(IWailaClientRegistration registration) {
 
 		registration.registerBlockComponent(this, Block.class);
 		registration.registerEntityComponent(this, Sentry.class);
+		registration.registerEntityComponent(this, SecuritySeaBoat.class);
 
 		registration.addBeforeRenderCallback((tooltip, rect, guiGraphics, accessor) -> ClientHandler.isPlayerMountedOnCamera());
 		registration.addRayTraceCallback((hit, accessor, original) -> {
@@ -67,7 +69,7 @@ public void appendTooltip(ITooltip tooltip, BlockAccessor data, IPluginConfig co
 		BlockState state = data.getBlockState();
 		Block block = data.getBlock();
 
-		addOwnerModuleNameInfo(level, pos, state, block, data.getBlockEntity(), data.getPlayer(), tooltip::add, config::get);
+		addDisguisedOwnerModuleNameInfo(level, pos, state, block, data.getBlockEntity(), data.getPlayer(), tooltip::add, config::get);
 
 		if (tooltip instanceof Tooltip t && block instanceof IOverlayDisplay overlayDisplay)
 			t.lines.get(0).alignedElements(Align.LEFT).set(0, new TextElement(Component.translatable(overlayDisplay.getDisplayStack(level, state, pos).getDescriptionId()).setStyle(ITEM_NAME_STYLE)));
diff --git a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/TOPDataProvider.java b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/TOPDataProvider.java
index 0ba1265ddb..7a7e45f677 100644
--- a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/TOPDataProvider.java
+++ b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/TOPDataProvider.java
@@ -65,7 +65,7 @@ public ResourceLocation getID() {
 			public void addProbeInfo(ProbeMode mode, IProbeInfo probeInfo, Player player, Level level, BlockState state, IProbeHitData data) {
 				BlockPos pos = data.getPos();
 
-				addOwnerModuleNameInfo(level, pos, state, state.getBlock(), level.getBlockEntity(pos), player, probeInfo::mcText, $ -> true);
+				addDisguisedOwnerModuleNameInfo(level, pos, state, state.getBlock(), level.getBlockEntity(pos), player, probeInfo::mcText, $ -> true);
 			}
 		});
 		theOneProbe.registerEntityProvider(new IProbeInfoEntityProvider() {
diff --git a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/WTHITDataProvider.java b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/WTHITDataProvider.java
index da0bf20544..2ef6eb1628 100644
--- a/src/main/java/net/geforcemods/securitycraft/compat/hudmods/WTHITDataProvider.java
+++ b/src/main/java/net/geforcemods/securitycraft/compat/hudmods/WTHITDataProvider.java
@@ -19,6 +19,7 @@
 import net.geforcemods.securitycraft.ClientHandler;
 import net.geforcemods.securitycraft.api.IOwnable;
 import net.geforcemods.securitycraft.compat.IOverlayDisplay;
+import net.geforcemods.securitycraft.entity.SecuritySeaBoat;
 import net.geforcemods.securitycraft.entity.sentry.Sentry;
 import net.geforcemods.securitycraft.util.Utils;
 import net.minecraft.client.gui.GuiGraphics;
@@ -38,6 +39,7 @@ public void register(IRegistrar registrar) {
 		registrar.addComponent((IBlockComponentProvider) this, TooltipPosition.TAIL, IOverlayDisplay.class);
 		registrar.addIcon((IBlockComponentProvider) this, IOverlayDisplay.class);
 		registrar.addComponent((IEntityComponentProvider) this, TooltipPosition.BODY, Sentry.class);
+		registrar.addComponent((IEntityComponentProvider) this, TooltipPosition.BODY, SecuritySeaBoat.class);
 	}
 
 	@Override
@@ -60,7 +62,7 @@ public void appendHead(ITooltip tooltip, IBlockAccessor data, IPluginConfig conf
 
 	@Override
 	public void appendBody(ITooltip tooltip, IBlockAccessor data, IPluginConfig config) {
-		addOwnerModuleNameInfo(data.getWorld(), data.getPosition(), data.getBlockState(), data.getBlock(), data.getBlockEntity(), data.getPlayer(), tooltip::addLine, config::getBoolean);
+		addDisguisedOwnerModuleNameInfo(data.getWorld(), data.getPosition(), data.getBlockState(), data.getBlock(), data.getBlockEntity(), data.getPlayer(), tooltip::addLine, config::getBoolean);
 	}
 
 	@Override

From c95d5c71604220be3e003d785688d246a52601e9 Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Mon, 20 May 2024 20:55:28 +0200
Subject: [PATCH 49/55] add entity name translation entries

---
 src/main/resources/assets/securitycraft/lang/de_at.json | 1 +
 src/main/resources/assets/securitycraft/lang/de_ch.json | 1 +
 src/main/resources/assets/securitycraft/lang/de_de.json | 1 +
 src/main/resources/assets/securitycraft/lang/en_us.json | 1 +
 4 files changed, 4 insertions(+)

diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json
index deb681bc02..1c140009f0 100644
--- a/src/main/resources/assets/securitycraft/lang/de_at.json
+++ b/src/main/resources/assets/securitycraft/lang/de_at.json
@@ -637,6 +637,7 @@
 	"death.attack.securitycraft.taser.item": "%1$s wurde von %2$s mit %3$s zu Tode getasert",
 	"entity.securitycraft.bullet": "Geschützkugel",
 	"entity.securitycraft.imsbomb": "I.M.S.-Bombe",
+	"entity.securitycraft.security_sea_boat": "Gesichertes Seeboot",
 	"entity.securitycraft.sentry": "Geschütz",
 	"gamerule.fakeLavaSourceConversion": "Erneuerbarkeit von Falscher Lava",
 	"gamerule.fakeWaterSourceConversion": "Erneuerbarkeit von Falschem Wasser",
diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json
index deb681bc02..1c140009f0 100644
--- a/src/main/resources/assets/securitycraft/lang/de_ch.json
+++ b/src/main/resources/assets/securitycraft/lang/de_ch.json
@@ -637,6 +637,7 @@
 	"death.attack.securitycraft.taser.item": "%1$s wurde von %2$s mit %3$s zu Tode getasert",
 	"entity.securitycraft.bullet": "Geschützkugel",
 	"entity.securitycraft.imsbomb": "I.M.S.-Bombe",
+	"entity.securitycraft.security_sea_boat": "Gesichertes Seeboot",
 	"entity.securitycraft.sentry": "Geschütz",
 	"gamerule.fakeLavaSourceConversion": "Erneuerbarkeit von Falscher Lava",
 	"gamerule.fakeWaterSourceConversion": "Erneuerbarkeit von Falschem Wasser",
diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json
index deb681bc02..1c140009f0 100644
--- a/src/main/resources/assets/securitycraft/lang/de_de.json
+++ b/src/main/resources/assets/securitycraft/lang/de_de.json
@@ -637,6 +637,7 @@
 	"death.attack.securitycraft.taser.item": "%1$s wurde von %2$s mit %3$s zu Tode getasert",
 	"entity.securitycraft.bullet": "Geschützkugel",
 	"entity.securitycraft.imsbomb": "I.M.S.-Bombe",
+	"entity.securitycraft.security_sea_boat": "Gesichertes Seeboot",
 	"entity.securitycraft.sentry": "Geschütz",
 	"gamerule.fakeLavaSourceConversion": "Erneuerbarkeit von Falscher Lava",
 	"gamerule.fakeWaterSourceConversion": "Erneuerbarkeit von Falschem Wasser",
diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json
index e3788c27e9..5835d47246 100644
--- a/src/main/resources/assets/securitycraft/lang/en_us.json
+++ b/src/main/resources/assets/securitycraft/lang/en_us.json
@@ -638,6 +638,7 @@
 	"death.attack.securitycraft.taser.item": "%1$s was tasered to death by %2$s using %3$s",
 	"entity.securitycraft.bullet": "Sentry Bullet",
 	"entity.securitycraft.imsbomb": "I.M.S. Bomb",
+	"entity.securitycraft.security_sea_boat": "Security Sea Boat",
 	"entity.securitycraft.sentry": "Sentry",
 	"gamerule.fakeLavaSourceConversion": "Fake lava converts to source",
 	"gamerule.fakeWaterSourceConversion": "Fake water converts to source",

From e4ffa5f05e3220d008571229e7cb518eaba2bbf9 Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Tue, 21 May 2024 00:07:52 +0200
Subject: [PATCH 50/55] fix module states not synchronizing correctly

---
 .../geforcemods/securitycraft/SCContent.java  |  4 ++
 .../securitycraft/entity/SecuritySeaBoat.java |  9 ++--
 .../misc/ItemStackListSerializer.java         | 43 +++++++++++++++++++
 3 files changed, 52 insertions(+), 4 deletions(-)
 create mode 100644 src/main/java/net/geforcemods/securitycraft/misc/ItemStackListSerializer.java

diff --git a/src/main/java/net/geforcemods/securitycraft/SCContent.java b/src/main/java/net/geforcemods/securitycraft/SCContent.java
index 164d291dbe..eaed9d32bd 100644
--- a/src/main/java/net/geforcemods/securitycraft/SCContent.java
+++ b/src/main/java/net/geforcemods/securitycraft/SCContent.java
@@ -231,6 +231,7 @@
 import net.geforcemods.securitycraft.items.UniversalOwnerChangerItem;
 import net.geforcemods.securitycraft.items.WireCuttersItem;
 import net.geforcemods.securitycraft.misc.BlockEntityNBTCondition;
+import net.geforcemods.securitycraft.misc.ItemStackListSerializer;
 import net.geforcemods.securitycraft.misc.LimitedUseKeycardRecipe;
 import net.geforcemods.securitycraft.misc.ModuleStatesSerializer;
 import net.geforcemods.securitycraft.misc.ModuleType;
@@ -248,6 +249,7 @@
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
 import net.minecraft.core.Holder;
+import net.minecraft.core.NonNullList;
 import net.minecraft.core.particles.ParticleType;
 import net.minecraft.core.particles.SimpleParticleType;
 import net.minecraft.core.registries.Registries;
@@ -262,6 +264,7 @@
 import net.minecraft.world.item.DyeColor;
 import net.minecraft.world.item.HangingSignItem;
 import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.SignItem;
 import net.minecraft.world.item.crafting.RecipeSerializer;
 import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
@@ -325,6 +328,7 @@ public class SCContent {
 	//data serializer entries
 	public static final DeferredHolder, EntityDataSerializer> OWNER_SERIALIZER = DATA_SERIALIZERS.register("owner", () -> new OwnerDataSerializer());
 	public static final DeferredHolder, EntityDataSerializer>> MODULE_STATES_SERIALIZER = DATA_SERIALIZERS.register("module_states", () -> new ModuleStatesSerializer());
+	public static final DeferredHolder, EntityDataSerializer>> ITEM_STACK_LIST_SERIALIZER = DATA_SERIALIZERS.register("item_stack_list", () -> new ItemStackListSerializer());
 
 	//particle types
 	public static final DeferredHolder, SimpleParticleType> FLOOR_TRAP_CLOUD = PARTICLE_TYPES.register("floor_trap_cloud", () -> new SimpleParticleType(false));
diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index 9016a209a6..cebe382c27 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -68,9 +68,9 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodePro
 	private static final EntityDataAccessor SEND_DENYLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN);
 	private static final EntityDataAccessor SMART_MODULE_COOLDOWN = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.INT);
 	private static final EntityDataAccessor> MODULE_STATES = SynchedEntityData.>defineId(SecuritySeaBoat.class, SCContent.MODULE_STATES_SERIALIZER.get());
+	private static final EntityDataAccessor> MODULES = SynchedEntityData.>defineId(SecuritySeaBoat.class, SCContent.ITEM_STACK_LIST_SERIALIZER.get());
 	private byte[] passcode;
 	private UUID saltKey;
-	private NonNullList modules = NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY);
 	private EntityDataWrappedOption> sendAllowlistMessage = new SendAllowlistMessageOption(false).wrapForEntityData(SEND_ALLOWLIST_MESSAGE, () -> entityData);
 	private EntityDataWrappedOption> sendDenylistMessage = new SendDenylistMessageOption(true).wrapForEntityData(SEND_DENYLIST_MESSAGE, () -> entityData);
 	private EntityDataWrappedOption> smartModuleCooldown = new SmartModuleCooldownOption().wrapForEntityData(SMART_MODULE_COOLDOWN, () -> entityData);
@@ -97,6 +97,7 @@ protected void defineSynchedData() {
 		entityData.define(SEND_DENYLIST_MESSAGE, true);
 		entityData.define(SMART_MODULE_COOLDOWN, 100);
 		entityData.define(MODULE_STATES, new EnumMap<>(ModuleType.class));
+		entityData.define(MODULES, NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY));
 	}
 
 	@Override
@@ -306,7 +307,7 @@ public Item getDropItem() {
 	@Override
 	public void remove(RemovalReason reason) {
 		if (!level().isClientSide && reason.shouldDestroy())
-			Containers.dropContents(level(), blockPosition(), modules);
+			Containers.dropContents(level(), blockPosition(), getInventory());
 
 		super.remove(reason);
 	}
@@ -339,7 +340,7 @@ protected void addAdditionalSaveData(CompoundTag tag) {
 	@Override
 	protected void readAdditionalSaveData(CompoundTag tag) {
 		super.readAdditionalSaveData(tag);
-		modules = readModuleInventory(tag);
+		entityData.set(MODULES, readModuleInventory(tag));
 		entityData.set(MODULE_STATES, readModuleStates(tag));
 		readOptions(tag);
 		cooldownEnd = System.currentTimeMillis() + tag.getLong("cooldownLeft");
@@ -443,7 +444,7 @@ public Option[] customOptions() {
 
 	@Override
 	public NonNullList getInventory() {
-		return modules;
+		return entityData.get(MODULES);
 	}
 
 	@Override
diff --git a/src/main/java/net/geforcemods/securitycraft/misc/ItemStackListSerializer.java b/src/main/java/net/geforcemods/securitycraft/misc/ItemStackListSerializer.java
new file mode 100644
index 0000000000..31f989af37
--- /dev/null
+++ b/src/main/java/net/geforcemods/securitycraft/misc/ItemStackListSerializer.java
@@ -0,0 +1,43 @@
+package net.geforcemods.securitycraft.misc;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.syncher.EntityDataAccessor;
+import net.minecraft.network.syncher.EntityDataSerializer;
+import net.minecraft.world.item.ItemStack;
+
+public class ItemStackListSerializer implements EntityDataSerializer> {
+	@Override
+	public void write(FriendlyByteBuf buf, NonNullList value) {
+		buf.writeVarInt(value.size());
+		value.forEach(buf::writeItem);
+	}
+
+	@Override
+	public NonNullList read(FriendlyByteBuf buf) {
+		int size = buf.readVarInt();
+		NonNullList modules = NonNullList.withSize(size, ItemStack.EMPTY);
+
+		for (int i = 0; i < size; i++) {
+			modules.set(i, buf.readItem());
+		}
+
+		return modules;
+	}
+
+	@Override
+	public EntityDataAccessor> createAccessor(int id) {
+		return new EntityDataAccessor<>(id, this);
+	}
+
+	@Override
+	public NonNullList copy(NonNullList value) {
+		NonNullList copy = NonNullList.withSize(value.size(), ItemStack.EMPTY);
+
+		for (int i = 0; i < value.size(); i++) {
+			copy.set(i, value.get(i));
+		}
+
+		return copy;
+	}
+}

From 87fdc638f600e789a91c134477b93147d9b3ee3b Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Tue, 21 May 2024 00:22:17 +0200
Subject: [PATCH 51/55] fix cooldown not showing up properly

---
 .../securitycraft/entity/SecuritySeaBoat.java            | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index cebe382c27..7f6d0c67da 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -67,6 +67,7 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodePro
 	private static final EntityDataAccessor SEND_ALLOWLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN);
 	private static final EntityDataAccessor SEND_DENYLIST_MESSAGE = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.BOOLEAN);
 	private static final EntityDataAccessor SMART_MODULE_COOLDOWN = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.INT);
+	private static final EntityDataAccessor COOLDOWN_END = SynchedEntityData.defineId(SecuritySeaBoat.class, EntityDataSerializers.LONG);
 	private static final EntityDataAccessor> MODULE_STATES = SynchedEntityData.>defineId(SecuritySeaBoat.class, SCContent.MODULE_STATES_SERIALIZER.get());
 	private static final EntityDataAccessor> MODULES = SynchedEntityData.>defineId(SecuritySeaBoat.class, SCContent.ITEM_STACK_LIST_SERIALIZER.get());
 	private byte[] passcode;
@@ -74,7 +75,6 @@ public class SecuritySeaBoat extends ChestBoat implements IOwnable, IPasscodePro
 	private EntityDataWrappedOption> sendAllowlistMessage = new SendAllowlistMessageOption(false).wrapForEntityData(SEND_ALLOWLIST_MESSAGE, () -> entityData);
 	private EntityDataWrappedOption> sendDenylistMessage = new SendDenylistMessageOption(true).wrapForEntityData(SEND_DENYLIST_MESSAGE, () -> entityData);
 	private EntityDataWrappedOption> smartModuleCooldown = new SmartModuleCooldownOption().wrapForEntityData(SMART_MODULE_COOLDOWN, () -> entityData);
-	private long cooldownEnd = 0;
 	private boolean isInLava = false;
 
 	public SecuritySeaBoat(EntityType type, Level level) {
@@ -96,6 +96,7 @@ protected void defineSynchedData() {
 		entityData.define(SEND_ALLOWLIST_MESSAGE, false);
 		entityData.define(SEND_DENYLIST_MESSAGE, true);
 		entityData.define(SMART_MODULE_COOLDOWN, 100);
+		entityData.define(COOLDOWN_END, 0L);
 		entityData.define(MODULE_STATES, new EnumMap<>(ModuleType.class));
 		entityData.define(MODULES, NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY));
 	}
@@ -343,7 +344,7 @@ protected void readAdditionalSaveData(CompoundTag tag) {
 		entityData.set(MODULES, readModuleInventory(tag));
 		entityData.set(MODULE_STATES, readModuleStates(tag));
 		readOptions(tag);
-		cooldownEnd = System.currentTimeMillis() + tag.getLong("cooldownLeft");
+		entityData.set(COOLDOWN_END, System.currentTimeMillis() + tag.getLong("cooldownLeft"));
 		entityData.set(OWNER, Owner.fromCompound(tag.getCompound("owner")));
 		loadSaltKey(tag);
 		loadPasscode(tag);
@@ -422,7 +423,7 @@ public void setSaltKey(UUID saltKey) {
 	@Override
 	public void startCooldown() {
 		if (!isOnCooldown())
-			cooldownEnd = System.currentTimeMillis() + smartModuleCooldown.get() * 50;
+			entityData.set(COOLDOWN_END, System.currentTimeMillis() + smartModuleCooldown.get() * 50);
 	}
 
 	@Override
@@ -432,7 +433,7 @@ public boolean isOnCooldown() {
 
 	@Override
 	public long getCooldownEnd() {
-		return cooldownEnd;
+		return entityData.get(COOLDOWN_END);
 	}
 
 	@Override

From efd6d500141a51c06dc5ec6412da70bf37aade4c Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Tue, 21 May 2024 14:46:25 +0200
Subject: [PATCH 52/55] add feedback for when a player can't enter a boat

---
 .../geforcemods/securitycraft/entity/SecuritySeaBoat.java | 8 ++++++++
 src/main/resources/assets/securitycraft/lang/de_at.json   | 1 +
 src/main/resources/assets/securitycraft/lang/de_ch.json   | 1 +
 src/main/resources/assets/securitycraft/lang/de_de.json   | 1 +
 src/main/resources/assets/securitycraft/lang/en_us.json   | 1 +
 5 files changed, 12 insertions(+)

diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index 7f6d0c67da..69cebf8da9 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -182,6 +182,14 @@ else if (stack.is(SCContent.UNIVERSAL_BLOCK_REMOVER.get())) {
 					PlayerUtils.sendMessageToPlayer(player, Utils.localize(SCContent.UNIVERSAL_BLOCK_REMOVER.get().getDescriptionId()), Utils.localize("messages.securitycraft:notOwned", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED);
 			}
 		}
+		else if (!canAddPassenger(player)) {
+			if (isDenied(player))
+				PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:module.onDenylist"), ChatFormatting.RED);
+			else
+				PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:security_sea_boat.cant_enter", PlayerUtils.getOwnerComponent(getOwner())), ChatFormatting.RED);
+
+			return InteractionResult.sidedSuccess(level.isClientSide);
+		}
 
 		return super.interact(player, hand);
 	}
diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json
index 1c140009f0..0da6e0c35e 100644
--- a/src/main/resources/assets/securitycraft/lang/de_at.json
+++ b/src/main/resources/assets/securitycraft/lang/de_at.json
@@ -1062,6 +1062,7 @@
 	"messages.securitycraft:security_camera.direction_set": "Standard-Blickrichtung geändert",
 	"messages.securitycraft:security_camera.no_permission": "Keine Erlaubnis, um die Standard-Blickrichtung zu ändern",
 	"messages.securitycraft:security_camera.smart_module_needed": "Schlaues Modul benötigt",
+	"messages.securitycraft:security_sea_boat.cant_enter": "Entschuldigung, du kannst nicht in das Boot einsteigen. Es gehört %s.",
 	"messages.securitycraft:sentry.descriptionMode0": "(Immer aktiv, attackiert Monster und Spieler)",
 	"messages.securitycraft:sentry.descriptionMode1": "(Aktiv, sobald ein Monster oder Spieler nahe ist, attackiert Monster und Spieler)",
 	"messages.securitycraft:sentry.descriptionMode2": "(Immer aktiv, attackiert Monster)",
diff --git a/src/main/resources/assets/securitycraft/lang/de_ch.json b/src/main/resources/assets/securitycraft/lang/de_ch.json
index 1c140009f0..0da6e0c35e 100644
--- a/src/main/resources/assets/securitycraft/lang/de_ch.json
+++ b/src/main/resources/assets/securitycraft/lang/de_ch.json
@@ -1062,6 +1062,7 @@
 	"messages.securitycraft:security_camera.direction_set": "Standard-Blickrichtung geändert",
 	"messages.securitycraft:security_camera.no_permission": "Keine Erlaubnis, um die Standard-Blickrichtung zu ändern",
 	"messages.securitycraft:security_camera.smart_module_needed": "Schlaues Modul benötigt",
+	"messages.securitycraft:security_sea_boat.cant_enter": "Entschuldigung, du kannst nicht in das Boot einsteigen. Es gehört %s.",
 	"messages.securitycraft:sentry.descriptionMode0": "(Immer aktiv, attackiert Monster und Spieler)",
 	"messages.securitycraft:sentry.descriptionMode1": "(Aktiv, sobald ein Monster oder Spieler nahe ist, attackiert Monster und Spieler)",
 	"messages.securitycraft:sentry.descriptionMode2": "(Immer aktiv, attackiert Monster)",
diff --git a/src/main/resources/assets/securitycraft/lang/de_de.json b/src/main/resources/assets/securitycraft/lang/de_de.json
index 1c140009f0..0da6e0c35e 100644
--- a/src/main/resources/assets/securitycraft/lang/de_de.json
+++ b/src/main/resources/assets/securitycraft/lang/de_de.json
@@ -1062,6 +1062,7 @@
 	"messages.securitycraft:security_camera.direction_set": "Standard-Blickrichtung geändert",
 	"messages.securitycraft:security_camera.no_permission": "Keine Erlaubnis, um die Standard-Blickrichtung zu ändern",
 	"messages.securitycraft:security_camera.smart_module_needed": "Schlaues Modul benötigt",
+	"messages.securitycraft:security_sea_boat.cant_enter": "Entschuldigung, du kannst nicht in das Boot einsteigen. Es gehört %s.",
 	"messages.securitycraft:sentry.descriptionMode0": "(Immer aktiv, attackiert Monster und Spieler)",
 	"messages.securitycraft:sentry.descriptionMode1": "(Aktiv, sobald ein Monster oder Spieler nahe ist, attackiert Monster und Spieler)",
 	"messages.securitycraft:sentry.descriptionMode2": "(Immer aktiv, attackiert Monster)",
diff --git a/src/main/resources/assets/securitycraft/lang/en_us.json b/src/main/resources/assets/securitycraft/lang/en_us.json
index 5835d47246..934e574002 100644
--- a/src/main/resources/assets/securitycraft/lang/en_us.json
+++ b/src/main/resources/assets/securitycraft/lang/en_us.json
@@ -1071,6 +1071,7 @@
 	"messages.securitycraft:security_camera.direction_set": "Default viewing direction set",
 	"messages.securitycraft:security_camera.no_permission": "No permission to set the default viewing direction",
 	"messages.securitycraft:security_camera.smart_module_needed": "Smart Module required",
+	"messages.securitycraft:security_sea_boat.cant_enter": "Sorry, you cannot enter this boat. It is owned by %s.",
 	"messages.securitycraft:sentry.descriptionMode0": "(Always active, attacks hostile mobs and players)",
 	"messages.securitycraft:sentry.descriptionMode1": "(Activates when a hostile mob or player comes near, attacks hostile mobs and players)",
 	"messages.securitycraft:sentry.descriptionMode2": "(Always active, attacks hostile mobs)",

From 307a3ae38a239f383b742fceb3578a8e9cb4ca93 Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Tue, 21 May 2024 15:01:12 +0200
Subject: [PATCH 53/55] make sure owners can't deny themselves

---
 .../net/geforcemods/securitycraft/entity/SecuritySeaBoat.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
index 69cebf8da9..f0293b4f64 100644
--- a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
+++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java
@@ -111,7 +111,7 @@ public InteractionResult interact(Player player, InteractionHand hand) {
 		ItemStack stack = player.getItemInHand(hand);
 		Level level = player.level();
 
-		if (isDenied(player) && !(player.isSecondaryUseActive() && stack.is(SCContent.CODEBREAKER.get()))) {
+		if (isDenied(player) && !isOwnedBy(player) && !(player.isSecondaryUseActive() && stack.is(SCContent.CODEBREAKER.get()))) {
 			if (sendsDenylistMessage())
 				PlayerUtils.sendMessageToPlayer(player, Utils.localize(getType().getDescriptionId()), Utils.localize("messages.securitycraft:module.onDenylist"), ChatFormatting.RED);
 

From fe74f2fdf438a7ab8319990294b0dab14cd6751c Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Tue, 21 May 2024 15:25:10 +0200
Subject: [PATCH 54/55] add reinforced tint to boat

---
 .../securitycraft/ClientHandler.java          |  2 +-
 .../renderers/SecuritySeaBoatModel.java       | 35 +++++++++++++++++++
 .../renderers/SecuritySeaBoatRenderer.java    | 15 ++++++++
 .../renderers/SecuritySeaRaftModel.java       | 35 +++++++++++++++++++
 4 files changed, 86 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatModel.java
 create mode 100644 src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftModel.java

diff --git a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java
index 4e3dee486b..969a5747a5 100644
--- a/src/main/java/net/geforcemods/securitycraft/ClientHandler.java
+++ b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java
@@ -636,7 +636,7 @@ public static void onRegisterColorHandlersItem(RegisterColorHandlersEvent.Item e
 		blocksWithCustomTint = null;
 	}
 
-	private static int mixWithReinforcedTintIfEnabled(int tint) {
+	public static int mixWithReinforcedTintIfEnabled(int tint) {
 		boolean tintReinforcedBlocks;
 
 		if (Minecraft.getInstance().level == null)
diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatModel.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatModel.java
new file mode 100644
index 0000000000..06f7fdcb07
--- /dev/null
+++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatModel.java
@@ -0,0 +1,35 @@
+package net.geforcemods.securitycraft.renderers;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+
+import net.geforcemods.securitycraft.ClientHandler;
+import net.minecraft.client.model.ChestBoatModel;
+import net.minecraft.client.model.geom.ModelPart;
+import net.minecraft.util.FastColor;
+
+public class SecuritySeaBoatModel extends ChestBoatModel {
+	public SecuritySeaBoatModel(ModelPart modelPart) {
+		super(modelPart);
+	}
+
+	@Override
+	public void renderToBuffer(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) {
+		int packedColor = FastColor.ARGB32.color((int) (alpha * 255.0F), (int) (red * 255.0F), (int) (green * 255.0F), (int) (blue * 255.0F));
+		int tinted = ClientHandler.mixWithReinforcedTintIfEnabled(packedColor);
+		float tintedRed = FastColor.ARGB32.red(tinted) / 255.0F;
+		float tintedGreen = FastColor.ARGB32.green(tinted) / 255.0F;
+		float tintedBlue = FastColor.ARGB32.blue(tinted) / 255.0F;
+		float tintedAlpha = FastColor.ARGB32.alpha(tinted) / 255.0F;
+		var parts = parts();
+
+		//The last three parts are the chest, and that one should not get tinted
+		for (int i = 0; i < parts.size() - 2; i++) {
+			parts.get(i).render(poseStack, buffer, packedLight, packedOverlay, tintedRed, tintedGreen, tintedBlue, tintedAlpha);
+		}
+
+		for (int i = parts.size() - 3; i < parts.size(); i++) {
+			parts.get(i).render(poseStack, buffer, packedLight, packedOverlay, red, green, blue, alpha);
+		}
+	}
+}
diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java
index e41e968069..e643ca9f20 100644
--- a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java
+++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java
@@ -6,10 +6,15 @@
 import com.mojang.datafixers.util.Pair;
 
 import net.geforcemods.securitycraft.SecurityCraft;
+import net.minecraft.client.model.ListModel;
+import net.minecraft.client.model.geom.ModelLayers;
+import net.minecraft.client.model.geom.ModelPart;
 import net.minecraft.client.renderer.entity.BoatRenderer;
 import net.minecraft.client.renderer.entity.EntityRendererProvider;
+import net.minecraft.client.renderer.entity.EntityRendererProvider.Context;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.world.entity.vehicle.Boat;
+import net.minecraft.world.entity.vehicle.Boat.Type;
 
 public class SecuritySeaBoatRenderer extends BoatRenderer {
 	public SecuritySeaBoatRenderer(EntityRendererProvider.Context ctx) {
@@ -20,4 +25,14 @@ public SecuritySeaBoatRenderer(EntityRendererProvider.Context ctx) {
                         type -> type,
                         type -> Pair.of(new ResourceLocation(SecurityCraft.MODID, "textures/entity/security_sea_boat/" + type.getName() + ".png"), createBoatModel(ctx, type, true))));
 	}
+
+	@Override
+	public ListModel createBoatModel(Context ctx, Type type, boolean chestBoat) {
+        ModelPart modelPart = ctx.bakeLayer(ModelLayers.createChestBoatModelName(type));
+
+        if (type == Boat.Type.BAMBOO)
+            return new SecuritySeaRaftModel(modelPart);
+         else
+            return new SecuritySeaBoatModel(modelPart);
+	}
 }
diff --git a/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftModel.java b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftModel.java
new file mode 100644
index 0000000000..dd2f182810
--- /dev/null
+++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaRaftModel.java
@@ -0,0 +1,35 @@
+package net.geforcemods.securitycraft.renderers;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+
+import net.geforcemods.securitycraft.ClientHandler;
+import net.minecraft.client.model.ChestRaftModel;
+import net.minecraft.client.model.geom.ModelPart;
+import net.minecraft.util.FastColor;
+
+public class SecuritySeaRaftModel extends ChestRaftModel {
+	public SecuritySeaRaftModel(ModelPart modelPart) {
+		super(modelPart);
+	}
+
+	@Override
+	public void renderToBuffer(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) {
+		int packedColor = FastColor.ARGB32.color((int) (alpha * 255.0F), (int) (red * 255.0F), (int) (green * 255.0F), (int) (blue * 255.0F));
+		int tinted = ClientHandler.mixWithReinforcedTintIfEnabled(packedColor);
+		float tintedRed = FastColor.ARGB32.red(tinted) / 255.0F;
+		float tintedGreen = FastColor.ARGB32.green(tinted) / 255.0F;
+		float tintedBlue = FastColor.ARGB32.blue(tinted) / 255.0F;
+		float tintedAlpha = FastColor.ARGB32.alpha(tinted) / 255.0F;
+		var parts = parts();
+
+		//The last three parts are the chest, and that one should not get tinted
+		for (int i = 0; i < parts.size() - 2; i++) {
+			parts.get(i).render(poseStack, buffer, packedLight, packedOverlay, tintedRed, tintedGreen, tintedBlue, tintedAlpha);
+		}
+
+		for (int i = parts.size() - 3; i < parts.size(); i++) {
+			parts.get(i).render(poseStack, buffer, packedLight, packedOverlay, red, green, blue, alpha);
+		}
+	}
+}

From 4a199260a729ecf8dcb1b746a54a21204d508be2 Mon Sep 17 00:00:00 2001
From: bl4ckscor3 
Date: Tue, 21 May 2024 17:38:40 +0200
Subject: [PATCH 55/55] update changelog

---
 Changelog.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Changelog.md b/Changelog.md
index 0682de14ff..c361c9995d 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,6 +2,7 @@
 
 - New: Server config setting "allow_camera_night_vision" to set whether players are able to activate night vision without having the actual potion effect
 - New: Pressing "Enter" while typing a player name in an Allowlist/Denylist Module will now add the player to the list without needing to press the "Add Player" button
+- New: Security Sea Boats: Chest boats with a passcode-protected chest
 - New: Damage Type Tag "securitycraft:security_sea_boat_vulnerable_to" to define which damage types the Security Sea Boat can be destroyed by
 - Change: The cameraSpeed client side config setting has been moved to be a per-block option, accessible with the Universal Block Modifier
 - Change: Some SecurityCraft tip messages have been reworded for clarity