diff --git a/Changelog.md b/Changelog.md index 7973312796..6fc583fde2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,8 @@ - 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 - Change: Increased suffocation damage inside reinforced blocks no longer affects non-player entities and players owning the reinforced blocks @@ -10,6 +12,11 @@ - Change: When picking up a placed sentry, the resulting sentry item will now be named according to the custom name of the removed sentry - 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 +- 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/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/oak_security_sea_boat.json b/src/generated/resources/assets/securitycraft/models/item/oak_security_sea_boat.json new file mode 100644 index 0000000000..2a53a99c48 --- /dev/null +++ b/src/generated/resources/assets/securitycraft/models/item/oak_security_sea_boat.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "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/oak_security_sea_boat.json b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/oak_security_sea_boat.json new file mode 100644 index 0000000000..0216693a12 --- /dev/null +++ b/src/generated/resources/data/securitycraft/advancements/recipes/transportation/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:oak_security_sea_boat" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_keypad_chest" + ] + ], + "rewards": { + "recipes": [ + "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/bamboo_security_sea_raft.json b/src/generated/resources/data/securitycraft/recipes/bamboo_security_sea_raft.json new file mode 100644 index 0000000000..f9f79f81b6 --- /dev/null +++ b/src/generated/resources/data/securitycraft/recipes/bamboo_security_sea_raft.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_bamboo_planks" + } + }, + "pattern": [ + "PCP", + "PPP" + ], + "result": { + "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/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/ClientHandler.java b/src/main/java/net/geforcemods/securitycraft/ClientHandler.java index 9bc700bd8e..969a5747a5 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; @@ -61,6 +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.SecuritySeaBoatRenderer; import net.geforcemods.securitycraft.renderers.SentryRenderer; import net.geforcemods.securitycraft.renderers.SonicSecuritySystemRenderer; import net.geforcemods.securitycraft.renderers.TrophySystemRenderer; @@ -120,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; @@ -384,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); @@ -414,6 +418,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_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); @@ -631,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) @@ -712,19 +717,31 @@ 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) { 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) { 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/RegistrationHandler.java b/src/main/java/net/geforcemods/securitycraft/RegistrationHandler.java index bd0dcfddb8..375f03bd30 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; @@ -218,13 +219,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 @@ -396,6 +399,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 f16f3b74a3..eaed9d32bd 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; @@ -178,6 +179,7 @@ import net.geforcemods.securitycraft.commands.SingleGameProfileArgument; import net.geforcemods.securitycraft.entity.BouncingBetty; import net.geforcemods.securitycraft.entity.IMSBomb; +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; @@ -217,6 +219,7 @@ import net.geforcemods.securitycraft.items.ModuleItem; import net.geforcemods.securitycraft.items.PortableTunePlayerItem; import net.geforcemods.securitycraft.items.SCManualItem; +import net.geforcemods.securitycraft.items.SecuritySeaBoatItem; import net.geforcemods.securitycraft.items.SentryItem; import net.geforcemods.securitycraft.items.SentryRemoteAccessToolItem; import net.geforcemods.securitycraft.items.SonicSecuritySystemItem; @@ -228,7 +231,9 @@ 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; import net.geforcemods.securitycraft.misc.OwnerDataSerializer; import net.geforcemods.securitycraft.misc.PageGroup; @@ -244,12 +249,14 @@ 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; 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; @@ -257,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; @@ -319,6 +327,8 @@ 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)); @@ -2628,6 +2638,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(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).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).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).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).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).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).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).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).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).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))); @@ -2918,12 +2946,19 @@ else if (state.is(BOUNCING_BETTY)) .setUpdateInterval(1) .setShouldReceiveVelocityUpdates(true) .build(SecurityCraft.MODID + ":bullet")); + 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_boat")); //@formatter:on //container types 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/SCCreativeModeTabs.java b/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java index 2238a81ae6..dde36f3e84 100644 --- a/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java +++ b/src/main/java/net/geforcemods/securitycraft/SCCreativeModeTabs.java @@ -131,6 +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.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/SCEventHandler.java b/src/main/java/net/geforcemods/securitycraft/SCEventHandler.java index 6d4849e9ef..bc314c54ec 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; @@ -352,7 +349,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; } @@ -568,56 +566,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) && !player.isCreative()) { - 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/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/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/ICodebreakable.java b/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java index 45df25bfdd..68c56b8d14 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java +++ b/src/main/java/net/geforcemods/securitycraft/api/ICodebreakable.java @@ -1,29 +1,85 @@ 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. + * Marks an object as being able to be hacked with the Codebreaker. * * @author Geforce */ 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) && !player.isCreative()) { + 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/ICustomizable.java b/src/main/java/net/geforcemods/securitycraft/api/ICustomizable.java index 0323dbc613..068a8d421b 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 object 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/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/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/IPasscodeProtected.java b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java
index 452d1b657f..6709c0b43a 100644
--- a/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java
+++ b/src/main/java/net/geforcemods/securitycraft/api/IPasscodeProtected.java
@@ -21,13 +21,12 @@
 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;
 
 /**
- * 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) { @@ -59,7 +58,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,8 +66,18 @@ default boolean verifyPasscodeSet(Level level, BlockPos pos, IOwnable ownable, P return false; } + /** + * 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)); + } + @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; @@ -78,19 +87,19 @@ default boolean shouldAttemptCodebreak(BlockState state, Player player) { } @Override - public default void useCodebreaker(BlockState state, Player player) { + public default void useCodebreaker(Player player) { activate(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 */ @@ -155,7 +164,7 @@ else if (SaltData.isKeyInUse(saltKey)) 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)); @@ -165,7 +174,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 */ @@ -174,29 +183,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(); diff --git a/src/main/java/net/geforcemods/securitycraft/api/Option.java b/src/main/java/net/geforcemods/securitycraft/api/Option.java index 770a2c06ac..c11fc36ed2 100644 --- a/src/main/java/net/geforcemods/securitycraft/api/Option.java +++ b/src/main/java/net/geforcemods/securitycraft/api/Option.java @@ -1,11 +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.world.level.block.Block; +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 @@ -44,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(); @@ -53,7 +60,7 @@ public void copy(Option option) { /** * @return This option's name. */ - public final String getName() { + public String getName() { return name; } @@ -110,19 +117,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"; } /** @@ -132,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. */ @@ -159,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); } @@ -167,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 { @@ -175,7 +198,7 @@ public DisabledOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.disabled"; } } @@ -186,7 +209,7 @@ public IgnoreOwnerOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.ignoreOwner"; } } @@ -197,7 +220,7 @@ public SendAllowlistMessageOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.sendAllowlistMessage"; } } @@ -208,7 +231,7 @@ public SendDenylistMessageOption(Boolean value) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.sendDenylistMessage"; } } @@ -233,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); } @@ -249,7 +272,7 @@ public SmartModuleCooldownOption() { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.smartModuleCooldown"; } } @@ -260,7 +283,7 @@ public SignalLengthOption(int defaultLength) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.signalLength"; } } @@ -285,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); } @@ -328,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); } } @@ -348,13 +372,126 @@ public TargetingModeOption(TargetingMode defaultValue) { } @Override - public String getKey(Block block) { + public String getKey(String denotation) { return "option.generic.targetingMode"; } @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/blockentities/AbstractKeypadFurnaceBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/AbstractKeypadFurnaceBlockEntity.java index 14f07a921d..918e38c24f 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 @@ -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/DisplayCaseBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java index a58853339e..69df067454 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/DisplayCaseBlockEntity.java @@ -67,13 +67,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/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/KeyPanelBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java index 1ccb0439d9..7e430fb907 100644 --- a/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java +++ b/src/main/java/net/geforcemods/securitycraft/blockentities/KeyPanelBlockEntity.java @@ -108,13 +108,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(AbstractPanelBlock.POWERED) && IPasscodeProtected.super.shouldAttemptCodebreak(state, player); + return !getBlockState().getValue(AbstractPanelBlock.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/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/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/KeypadChestBlockEntity.java b/src/main/java/net/geforcemods/securitycraft/blockentities/KeypadChestBlockEntity.java index 6a99737ed0..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 @@ -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/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 0601747dda..c33bddf493 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/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..d1e745c766 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; @@ -110,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); } @@ -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/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/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/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 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/datagen/RecipeGenerator.java b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java index 1b17a53226..d7a3d2ef38 100644 --- a/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java +++ b/src/main/java/net/geforcemods/securitycraft/datagen/RecipeGenerator.java @@ -881,6 +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_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()); @@ -1564,6 +1573,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); } diff --git a/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java new file mode 100644 index 0000000000..f0293b4f64 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/entity/SecuritySeaBoat.java @@ -0,0 +1,509 @@ +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.EntityDataWrappedOption; +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.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; +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; +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.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.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 { + 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 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; + private UUID saltKey; + 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 boolean isInLava = false; + + public SecuritySeaBoat(EntityType type, Level level) { + super(SCContent.SECURITY_SEA_BOAT_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; + zo = z; + } + + @Override + 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); + entityData.define(COOLDOWN_END, 0L); + entityData.define(MODULE_STATES, new EnumMap<>(ModuleType.class)); + entityData.define(MODULES, NonNullList.withSize(getMaxNumberOfModules(), ItemStack.EMPTY)); + } + + @Override + public boolean canAddPassenger(Entity passenger) { + return super.canAddPassenger(passenger) && (isOwnedBy(passenger) || isAllowed(passenger)) && !isDenied(passenger); + } + + @Override + public InteractionResult interact(Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + Level level = player.level(); + + 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); + + return InteractionResult.sidedSuccess(level.isClientSide); + } + + if (player.isSecondaryUseActive()) { + 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 (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())); + 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 (!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); + } + + return InteractionResult.sidedSuccess(level.isClientSide); + } + 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); + } + 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); + } + } + 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); + } + + @Override + public InteractionResult interactWithContainerVehicle(Player player) { + Level level = level(); + BlockPos pos = blockPosition(); + + 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; + } + + @Override + 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())); + } + + @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) { + if (!passenger.fireImmune()) { + passenger.setRemainingFireTicks(passenger.getRemainingFireTicks() + 1); + + if (passenger.getRemainingFireTicks() == 0) + passenger.setSecondsOnFire(8); + } + + passenger.hurt(level().damageSources().inFire(), 1.0F); + } + } + } + + @Override + public boolean hurt(DamageSource source, float amount) { + Entity entity = source.getEntity(); + + if (!(entity instanceof Player player) || isOwnedBy(player) || player.isCreative()) + return super.hurt(source, amount); + else + 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); + 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(), getInventory()); + + 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(); + 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); + + 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(MODULES, readModuleInventory(tag)); + entityData.set(MODULE_STATES, readModuleStates(tag)); + readOptions(tag); + entityData.set(COOLDOWN_END, System.currentTimeMillis() + tag.getLong("cooldownLeft")); + entityData.set(OWNER, Owner.fromCompound(tag.getCompound("owner"))); + loadSaltKey(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) { + if (player != null) + 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 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() { + 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() { + if (!isOnCooldown()) + entityData.set(COOLDOWN_END, System.currentTimeMillis() + smartModuleCooldown.get() * 50); + } + + @Override + public boolean isOnCooldown() { + return System.currentTimeMillis() < getCooldownEnd(); + } + + @Override + public long getCooldownEnd() { + return entityData.get(COOLDOWN_END); + } + + @Override + public Option[] customOptions() { + return new Option[] { + sendAllowlistMessage, sendDenylistMessage, smartModuleCooldown + }; + } + + @Override + public NonNullList getInventory() { + return entityData.get(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) && 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 + public String getModuleDescriptionId(String denotation, ModuleType module) { + return IModuleInventory.super.getModuleDescriptionId("generic." + denotation, module); + } + + @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 6fc5f35604..f8146ef2e6 100644 --- a/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java +++ b/src/main/java/net/geforcemods/securitycraft/inventory/CustomizeBlockMenu.java @@ -19,13 +19,26 @@ public class CustomizeBlockMenu extends AbstractContainerMenu { public final IModuleInventory moduleInv; private ContainerLevelAccess worldPosCallable; - public final int maxSlots; + private int maxSlots; + public final int entityId; 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); + entityId = -1; + } + + public CustomizeBlockMenu(int windowId, Level level, BlockPos pos, int entityId, Inventory inventory) { + 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) { int slotId = 0; for (int i = 0; i < 3; i++) { @@ -110,7 +123,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/items/BriefcaseItem.java b/src/main/java/net/geforcemods/securitycraft/items/BriefcaseItem.java index cc22c1e00d..0f83a9d996 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/items/SecuritySeaBoatItem.java b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaBoatItem.java new file mode 100644 index 0000000000..785c7574cd --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/items/SecuritySeaBoatItem.java @@ -0,0 +1,46 @@ +package net.geforcemods.securitycraft.items; + +import net.geforcemods.securitycraft.entity.SecuritySeaBoat; +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; + +public class SecuritySeaBoatItem extends BoatItem { + public SecuritySeaBoatItem(Boat.Type type, Item.Properties properties) { + super(true, type, 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(); + SecuritySeaBoat boat = new SecuritySeaBoat(level, vec3.x, vec3.y, vec3.z); + + if (level instanceof ServerLevel serverLevel) + EntityType.createDefaultStackConfig(serverLevel, stack, player).accept(boat); + + boat.setOwner(player); + return boat; + } +} 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/items/UniversalKeyChangerItem.java b/src/main/java/net/geforcemods/securitycraft/items/UniversalKeyChangerItem.java index 570349c1b3..7cb92a00ce 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/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; + } +} 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); + } +} 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/misc/SCManualPage.java b/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java index bf2cb49f9d..b0b1ea6942 100644 --- a/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java +++ b/src/main/java/net/geforcemods/securitycraft/misc/SCManualPage.java @@ -1,6 +1,27 @@ 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(); + + 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); + + return null; + } +} 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(); + } + } +} 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..a8b7f34fac 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.CHANGE_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY || 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.CHANGE_PASSCODE_FOR_ENTITY || dataType == DataType.CHECK_PASSCODE_FOR_ENTITY || dataType == DataType.SET_PASSCODE_FOR_ENTITY) + buf.writeVarInt(entityId); } @Override @@ -78,17 +87,32 @@ public void handle(PlayPayloadContext ctx) { ClientHandler.displayAlarmScreen(be); break; - case CHECK_BRIEFCASE_PASSCODE: - ItemStack briefcaseStack = PlayerUtils.getItemStackFromAnyHand(ClientHandler.getClientPlayer(), SCContent.BRIEFCASE.get()); + case CHANGE_PASSCODE: + if (Minecraft.getInstance().level.getBlockEntity(pos) instanceof IPasscodeProtected be) + ClientHandler.displayUniversalKeyChangerScreen((BlockEntity) be); - if (!briefcaseStack.isEmpty()) - ClientHandler.displayBriefcasePasscodeScreen(briefcaseStack.getHoverName()); + 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) 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_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) @@ -104,27 +128,27 @@ 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); + 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) 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()); @@ -133,14 +157,17 @@ public void handle(PlayPayloadContext ctx) { public enum DataType { ALARM(true), - CHECK_BRIEFCASE_PASSCODE(false), + CHANGE_PASSCODE(true), + CHANGE_PASSCODE_FOR_ENTITY(false), CHECK_PASSCODE(true), + CHECK_PASSCODE_FOR_BRIEFCASE(false), + CHECK_PASSCODE_FOR_ENTITY(false), RIFT_STABILIZER(true), SENTRY_REMOTE_ACCESS_TOOL(false), - SET_BRIEFCASE_PASSCODE(false), SET_PASSCODE(true), - SONIC_SECURITY_SYSTEM(true), - UNIVERSAL_KEY_CHANGER(true); + SET_PASSCODE_FOR_BRIEFCASE(false), + SET_PASSCODE_FOR_ENTITY(false), + SONIC_SECURITY_SYSTEM(true); public final boolean needsPosition; 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/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/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/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/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 new file mode 100644 index 0000000000..e643ca9f20 --- /dev/null +++ b/src/main/java/net/geforcemods/securitycraft/renderers/SecuritySeaBoatRenderer.java @@ -0,0 +1,38 @@ +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.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) { + 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)))); + } + + @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); + } + } +} 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 { diff --git a/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/CustomizeBlockScreen.java index 6e3d2da771..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; @@ -19,6 +18,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 +35,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 +54,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,32 +85,40 @@ 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) { 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) { 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())); + + 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) { 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())); + + 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())); }); } @@ -139,7 +144,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 +206,10 @@ private void moduleButtonClicked(Button button) { moduleInv.insertModule(moduleInv.getModule(moduleType), true); } - PacketDistributor.SERVER.noArg().send(new ToggleModule(moduleInv.getBlockEntity().getBlockPos(), 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) { @@ -209,13 +217,18 @@ 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)); + + if (menu.entityId == -1) + PacketDistributor.SERVER.noArg().send(new ToggleOption(moduleInv.myPos(), i)); + else + PacketDistributor.SERVER.noArg().send(new ToggleOption(menu.entityId, i)); + return; } } @@ -226,27 +239,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", option.getValueText())); } private Component getOptionButtonTitle(Option option) { - return Utils.localize(option.getKey(block), 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 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); } diff --git a/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java b/src/main/java/net/geforcemods/securitycraft/screen/SCManualScreen.java index 12ba3214ca..c6a1dd29b6 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; @@ -42,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; @@ -62,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; @@ -474,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(block)), 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(block.getDescriptionId().substring(6), 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/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(); } } diff --git a/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java b/src/main/java/net/geforcemods/securitycraft/util/BlockUtils.java index 6ffebee49c..e05e8e248e 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; @@ -73,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); } } @@ -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 ""; + } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 973cffba38..c56bb3b7d8 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -66,3 +66,7 @@ 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; +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 diff --git a/src/main/resources/assets/securitycraft/lang/de_at.json b/src/main/resources/assets/securitycraft/lang/de_at.json index a884515150..0da6e0c35e 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", @@ -747,7 +748,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 +759,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:", @@ -789,6 +790,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,6 +898,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 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.", @@ -911,18 +914,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", @@ -934,6 +943,8 @@ "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", @@ -943,6 +954,7 @@ "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", @@ -1033,10 +1045,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.", @@ -1050,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)", @@ -1095,6 +1108,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 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 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.", @@ -1212,15 +1229,15 @@ "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", "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 a884515150..0da6e0c35e 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", @@ -747,7 +748,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 +759,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:", @@ -789,6 +790,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,6 +898,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 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.", @@ -911,18 +914,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", @@ -934,6 +943,8 @@ "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", @@ -943,6 +954,7 @@ "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", @@ -1033,10 +1045,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.", @@ -1050,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)", @@ -1095,6 +1108,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 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 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.", @@ -1212,15 +1229,15 @@ "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", "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 a884515150..0da6e0c35e 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", @@ -747,7 +748,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 +759,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:", @@ -789,6 +790,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,6 +898,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 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.", @@ -911,18 +914,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", @@ -934,6 +943,8 @@ "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", @@ -943,6 +954,7 @@ "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", @@ -1033,10 +1045,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.", @@ -1050,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)", @@ -1095,6 +1108,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 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 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.", @@ -1212,15 +1229,15 @@ "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", "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 3ce9248971..934e574002 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", @@ -754,7 +755,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 +767,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:", @@ -797,6 +798,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,6 +906,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. 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.", @@ -919,18 +922,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", @@ -942,6 +951,8 @@ "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", @@ -951,6 +962,7 @@ "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", @@ -1042,10 +1054,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.", @@ -1059,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)", @@ -1104,6 +1117,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 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.", @@ -1227,9 +1244,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/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 0000000000..01384e9d76 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/acacia.png differ diff --git a/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/bamboo.png b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/bamboo.png new file mode 100644 index 0000000000..7e39dc083d Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/bamboo.png differ 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 0000000000..68bf33e716 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/birch.png differ 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 0000000000..8f21a2243c Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/cherry.png differ 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 0000000000..ce1b9f0c05 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/dark_oak.png differ 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 0000000000..3c44982627 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/jungle.png differ 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 0000000000..e2b7582368 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/mangrove.png differ 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 0000000000..e7c6935fba Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/oak.png differ 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 0000000000..40a17bb167 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/entity/security_sea_boat/spruce.png differ diff --git a/src/main/resources/assets/securitycraft/textures/item/acacia_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/acacia_security_sea_boat.png new file mode 100644 index 0000000000..dcb06b2cc6 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/acacia_security_sea_boat.png differ diff --git a/src/main/resources/assets/securitycraft/textures/item/bamboo_security_sea_raft.png b/src/main/resources/assets/securitycraft/textures/item/bamboo_security_sea_raft.png new file mode 100644 index 0000000000..851fb7fcf8 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/bamboo_security_sea_raft.png differ 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 0000000000..913ecdbd82 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/birch_security_sea_boat.png differ 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 0000000000..3d7cd856ef Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/cherry_security_sea_boat.png differ diff --git a/src/main/resources/assets/securitycraft/textures/item/dark_oak_security_sea_boat.png b/src/main/resources/assets/securitycraft/textures/item/dark_oak_security_sea_boat.png new file mode 100644 index 0000000000..861ec875a5 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/dark_oak_security_sea_boat.png differ 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 0000000000..93d5c97c64 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/jungle_security_sea_boat.png differ 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 0000000000..e7afdac5f6 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/mangrove_security_sea_boat.png differ 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 0000000000..9c9977b6a7 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/oak_security_sea_boat.png differ 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 0000000000..24564566a7 Binary files /dev/null and b/src/main/resources/assets/securitycraft/textures/item/spruce_security_sea_boat.png differ 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",