├── src └── main │ ├── resources │ ├── data │ │ ├── locks │ │ │ ├── recipes │ │ │ │ ├── key.json │ │ │ │ ├── spring.json │ │ │ │ ├── key_ring.json │ │ │ │ ├── wood_lock_pick.json │ │ │ │ ├── gold_lock_pick.json │ │ │ │ ├── iron_lock_pick.json │ │ │ │ ├── steel_lock_pick.json │ │ │ │ ├── diamond_lock_pick.json │ │ │ │ ├── key_blank.json │ │ │ │ ├── wood_lock.json │ │ │ │ ├── gold_lock.json │ │ │ │ ├── iron_lock.json │ │ │ │ ├── steel_lock.json │ │ │ │ ├── diamond_lock.json │ │ │ │ ├── iron_lock_mechanism.json │ │ │ │ ├── wood_lock_mechanism.json │ │ │ │ └── steel_lock_mechanism.json │ │ │ ├── tags │ │ │ │ └── items │ │ │ │ │ ├── keys.json │ │ │ │ │ ├── locks.json │ │ │ │ │ └── lock_picks.json │ │ │ └── loot_tables │ │ │ │ └── inject │ │ │ │ └── chests │ │ │ │ ├── underwater_ruin_small.json │ │ │ │ ├── shipwreck_supply.json │ │ │ │ ├── jungle_temple.json │ │ │ │ ├── pillager_outpost.json │ │ │ │ ├── stronghold_corridor.json │ │ │ │ ├── stronghold_crossing.json │ │ │ │ ├── village │ │ │ │ └── village_toolsmith.json │ │ │ │ ├── shipwreck_treasure.json │ │ │ │ ├── desert_pyramid.json │ │ │ │ ├── simple_dungeon.json │ │ │ │ ├── underwater_ruin_big.json │ │ │ │ ├── woodland_mansion.json │ │ │ │ ├── abandoned_mineshaft.json │ │ │ │ └── buried_treasure.json │ │ └── forge │ │ │ └── tags │ │ │ └── items │ │ │ ├── ingots │ │ │ └── steel.json │ │ │ └── nuggets │ │ │ └── steel.json │ ├── logo.png │ ├── pack.mcmeta │ ├── assets │ │ └── locks │ │ │ ├── sounds │ │ │ ├── shock1.ogg │ │ │ ├── shock2.ogg │ │ │ ├── pin.fail.ogg │ │ │ ├── key_ring1.ogg │ │ │ ├── key_ring2.ogg │ │ │ ├── lock.close1.ogg │ │ │ ├── lock.close2.ogg │ │ │ ├── lock.open.ogg │ │ │ ├── lock.rattle.ogg │ │ │ ├── pin.match1.ogg │ │ │ └── pin.match2.ogg │ │ │ ├── models │ │ │ └── item │ │ │ │ ├── key.json │ │ │ │ ├── spring.json │ │ │ │ ├── key_blank.json │ │ │ │ ├── key_ring_1.json │ │ │ │ ├── key_ring_2.json │ │ │ │ ├── key_ring_3.json │ │ │ │ ├── master_key.json │ │ │ │ ├── gold_lock_pick.json │ │ │ │ ├── iron_lock_pick.json │ │ │ │ ├── open_gold_lock.json │ │ │ │ ├── open_iron_lock.json │ │ │ │ ├── open_wood_lock.json │ │ │ │ ├── wood_lock_pick.json │ │ │ │ ├── open_steel_lock.json │ │ │ │ ├── steel_lock_pick.json │ │ │ │ ├── diamond_lock_pick.json │ │ │ │ ├── iron_lock_mechanism.json │ │ │ │ ├── open_diamond_lock.json │ │ │ │ ├── wood_lock_mechanism.json │ │ │ │ ├── steel_lock_mechanism.json │ │ │ │ ├── gold_lock.json │ │ │ │ ├── iron_lock.json │ │ │ │ ├── wood_lock.json │ │ │ │ ├── steel_lock.json │ │ │ │ ├── diamond_lock.json │ │ │ │ └── key_ring.json │ │ │ ├── textures │ │ │ ├── item │ │ │ │ ├── key.png │ │ │ │ ├── spring.png │ │ │ │ ├── gold_lock.png │ │ │ │ ├── iron_lock.png │ │ │ │ ├── key_blank.png │ │ │ │ ├── wood_lock.png │ │ │ │ ├── key_ring_0.png │ │ │ │ ├── key_ring_1.png │ │ │ │ ├── key_ring_2.png │ │ │ │ ├── key_ring_3.png │ │ │ │ ├── master_key.png │ │ │ │ ├── steel_lock.png │ │ │ │ ├── diamond_lock.png │ │ │ │ ├── gold_lock_pick.png │ │ │ │ ├── iron_lock_pick.png │ │ │ │ ├── open_gold_lock.png │ │ │ │ ├── open_iron_lock.png │ │ │ │ ├── open_wood_lock.png │ │ │ │ ├── wood_lock_pick.png │ │ │ │ ├── open_steel_lock.png │ │ │ │ ├── steel_lock_pick.png │ │ │ │ ├── diamond_lock_pick.png │ │ │ │ ├── iron_lock_mechanism.png │ │ │ │ ├── open_diamond_lock.png │ │ │ │ ├── wood_lock_mechanism.png │ │ │ │ └── steel_lock_mechanism.png │ │ │ └── gui │ │ │ │ ├── gold_lock.png │ │ │ │ ├── iron_lock.png │ │ │ │ ├── steel_lock.png │ │ │ │ ├── wood_lock.png │ │ │ │ ├── diamond_lock.png │ │ │ │ ├── gold_lock_pick.png │ │ │ │ ├── iron_lock_pick.png │ │ │ │ ├── steel_lock_pick.png │ │ │ │ ├── wood_lock_pick.png │ │ │ │ └── diamond_lock_pick.png │ │ │ ├── sounds.json │ │ │ └── lang │ │ │ └── en_us.json │ ├── locks.mixins.json │ └── META-INF │ │ ├── accesstransformer.cfg │ │ └── mods.toml │ └── java │ └── melonslise │ └── locks │ ├── common │ ├── util │ │ ├── BiIntFunction.java │ │ ├── BiIntPredicate.java │ │ ├── ILockableProvider.java │ │ ├── LocksPredicates.java │ │ ├── LockableInfo.java │ │ ├── Transform.java │ │ ├── Lock.java │ │ ├── LocksUtil.java │ │ └── Lockable.java │ ├── capability │ │ ├── ISelection.java │ │ ├── ILockableStorage.java │ │ ├── Selection.java │ │ ├── EmptyCapabilityStorage.java │ │ ├── ILockableHandler.java │ │ ├── SerializableCapabilityProvider.java │ │ ├── CapabilityStorage.java │ │ ├── CapabilityProvider.java │ │ ├── LockableStorage.java │ │ ├── KeyRingInventory.java │ │ └── LockableHandler.java │ ├── init │ │ ├── LocksDamageSources.java │ │ ├── LocksConfiguredPlacements.java │ │ ├── LocksItemTags.java │ │ ├── LocksPacketDistributors.java │ │ ├── LocksConfiguredFeatures.java │ │ ├── LocksFeatures.java │ │ ├── LocksPlacements.java │ │ ├── LocksSoundEvents.java │ │ ├── LocksRecipeSerializers.java │ │ ├── LocksContainerTypes.java │ │ ├── LocksEnchantments.java │ │ ├── LocksNetwork.java │ │ ├── LocksCapabilities.java │ │ └── LocksItems.java │ ├── config │ │ ├── LocksClientConfig.java │ │ ├── LocksServerConfig.java │ │ └── LocksConfig.java │ ├── enchantment │ │ ├── SturdyEnchantment.java │ │ ├── ComplexityEnchantment.java │ │ └── ShockingEnchantment.java │ ├── event │ │ └── LocksModEvents.java │ ├── worldgen │ │ ├── ChestPlacement.java │ │ └── LockChestsFeature.java │ ├── network │ │ ├── toclient │ │ │ ├── RemoveLockablePacket.java │ │ │ ├── AddLockablePacket.java │ │ │ ├── TryPinResultPacket.java │ │ │ ├── UpdateLockablePacket.java │ │ │ └── AddLockableToChunkPacket.java │ │ └── toserver │ │ │ └── TryPinPacket.java │ ├── item │ │ ├── MasterKeyItem.java │ │ ├── KeyItem.java │ │ ├── LockingItem.java │ │ ├── KeyRingItem.java │ │ └── LockPickItem.java │ ├── recipe │ │ └── KeyRecipe.java │ └── container │ │ └── KeyRingContainer.java │ ├── mixin │ ├── LootPoolAccessor.java │ ├── LootTableAccessor.java │ ├── ForgeHooksAccessor.java │ ├── ChunkPrimerMixin.java │ ├── WorldMixin.java │ ├── PistonBlockStructureHelperMixin.java │ ├── HopperTileEntityMixin.java │ ├── LootTableManagerMixin.java │ ├── PistonBlockMixin.java │ ├── ChunkManagerMixin.java │ ├── ChestTileEntityMixin.java │ ├── LockableTileEntityMixin.java │ ├── AbstractFurnaceTileEntityMixin.java │ ├── ChestBlockMixin.java │ ├── ExplosionContextMixin.java │ ├── client │ │ └── WorldRendererMixin.java │ ├── ChunkMixin.java │ ├── ServerWorldMixin.java │ └── TemplateMixin.java │ ├── client │ ├── gui │ │ ├── sprite │ │ │ ├── action │ │ │ │ ├── WaitAction.java │ │ │ │ ├── IAction.java │ │ │ │ ├── TimedAction.java │ │ │ │ ├── SingleCallbackAction.java │ │ │ │ ├── FadeAction.java │ │ │ │ ├── MoveAction.java │ │ │ │ └── AccelerateAction.java │ │ │ ├── SpringSprite.java │ │ │ ├── TextureInfo.java │ │ │ └── Sprite.java │ │ └── KeyRingScreen.java │ ├── init │ │ ├── LocksScreens.java │ │ ├── LocksRenderTypes.java │ │ └── LocksItemModelsProperties.java │ └── event │ │ └── LocksClientModEvents.java │ └── Locks.java └── README.md /src/main/resources/data/locks/recipes/key.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "locks:crafting_key" 3 | } -------------------------------------------------------------------------------- /src/main/resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/logo.png -------------------------------------------------------------------------------- /src/main/resources/data/forge/tags/items/ingots/steel.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": 4 | [ 5 | ] 6 | } -------------------------------------------------------------------------------- /src/main/resources/data/forge/tags/items/nuggets/steel.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": 4 | [ 5 | ] 6 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/tags/items/keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": 4 | [ 5 | "locks:key" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": 3 | { 4 | "description": "Locks resources", 5 | "pack_format": 6 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/shock1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/shock1.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/shock2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/shock2.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/key.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/key" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/pin.fail.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/pin.fail.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/spring.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/spring" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/key_ring1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/key_ring1.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/key_ring2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/key_ring2.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/lock.close1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/lock.close1.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/lock.close2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/lock.close2.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/lock.open.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/lock.open.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/lock.rattle.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/lock.rattle.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/pin.match1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/pin.match1.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds/pin.match2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/sounds/pin.match2.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/key.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/key_blank.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/key_blank" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/spring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/spring.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/key_ring_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/key_ring_1" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/key_ring_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/key_ring_2" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/key_ring_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/key_ring_3" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/master_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/master_key" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/gold_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/gold_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/iron_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/iron_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/steel_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/steel_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/wood_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/wood_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/gold_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/gold_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/iron_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/iron_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/key_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/key_blank.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/wood_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/wood_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/gold_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/gold_lock_pick" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/iron_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/iron_lock_pick" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/open_gold_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/open_gold_lock" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/open_iron_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/open_iron_lock" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/open_wood_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/open_wood_lock" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/wood_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/wood_lock_pick" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/diamond_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/diamond_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/key_ring_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/key_ring_0.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/key_ring_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/key_ring_1.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/key_ring_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/key_ring_2.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/key_ring_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/key_ring_3.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/master_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/master_key.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/steel_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/steel_lock.png -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/BiIntFunction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | public interface BiIntFunction 4 | { 5 | T apply(int x, int y); 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/open_steel_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/open_steel_lock" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/steel_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/steel_lock_pick" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/gold_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/gold_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/iron_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/iron_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/steel_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/steel_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/wood_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/wood_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/diamond_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/diamond_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/gold_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/gold_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/iron_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/iron_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/open_gold_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/open_gold_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/open_iron_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/open_iron_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/open_wood_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/open_wood_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/wood_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/wood_lock_pick.png -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/BiIntPredicate.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | public interface BiIntPredicate 4 | { 5 | boolean test(int x, int y); 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/diamond_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/diamond_lock_pick" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/iron_lock_mechanism.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/iron_lock_mechanism" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/open_diamond_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/open_diamond_lock" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/wood_lock_mechanism.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/wood_lock_mechanism" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/gui/diamond_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/gui/diamond_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/open_steel_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/open_steel_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/steel_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/steel_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/steel_lock_mechanism.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/steel_lock_mechanism" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/diamond_lock_pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/diamond_lock_pick.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/iron_lock_mechanism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/iron_lock_mechanism.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/open_diamond_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/open_diamond_lock.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/wood_lock_mechanism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/wood_lock_mechanism.png -------------------------------------------------------------------------------- /src/main/resources/assets/locks/textures/item/steel_lock_mechanism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MercurialPony/Locks/HEAD/src/main/resources/assets/locks/textures/item/steel_lock_mechanism.png -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/ILockableProvider.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import java.util.List; 4 | 5 | public interface ILockableProvider 6 | { 7 | List getLockables(); 8 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/tags/items/locks.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": 4 | [ 5 | "locks:wood_lock", 6 | "locks:iron_lock", 7 | "locks:steel_lock", 8 | "locks:gold_lock", 9 | "locks:diamond_lock" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/ISelection.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import net.minecraft.util.math.BlockPos; 4 | 5 | public interface ISelection 6 | { 7 | BlockPos get(); 8 | 9 | void set(BlockPos pos); 10 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/tags/items/lock_picks.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": 4 | [ 5 | "locks:wood_lock_pick", 6 | "locks:iron_lock_pick", 7 | "locks:steel_lock_pick", 8 | "locks:gold_lock_pick", 9 | "locks:diamond_lock_pick" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/spring.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | "N", 6 | "N", 7 | "N" 8 | ], 9 | "key": 10 | { 11 | "N": 12 | { 13 | "tag": "forge:nuggets/iron" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:spring" 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/key_ring.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " I ", 6 | "I I", 7 | " I " 8 | ], 9 | "key": 10 | { 11 | "I": 12 | { 13 | "tag": "forge:nuggets/iron" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:key_ring" 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/gold_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/gold_lock" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "locks:open": 1 13 | }, 14 | "model": "locks:item/open_gold_lock" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/iron_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/iron_lock" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "locks:open": 1 13 | }, 14 | "model": "locks:item/open_iron_lock" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/wood_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/wood_lock" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "locks:open": 1 13 | }, 14 | "model": "locks:item/open_wood_lock" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/steel_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/steel_lock" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "locks:open": 1 13 | }, 14 | "model": "locks:item/open_steel_lock" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/wood_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " S ", 6 | "S S", 7 | "SS " 8 | ], 9 | "key": 10 | { 11 | "S": 12 | { 13 | "item": "minecraft:stick" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:wood_lock_pick" 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/diamond_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/diamond_lock" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "locks:open": 1 13 | }, 14 | "model": "locks:item/open_diamond_lock" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksDamageSources.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import net.minecraft.util.DamageSource; 4 | 5 | public final class LocksDamageSources 6 | { 7 | public static final DamageSource SHOCK = new DamageSource("locks.shock").bypassArmor(); 8 | 9 | private LocksDamageSources() {} 10 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/gold_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " GG", 6 | " G ", 7 | "G " 8 | ], 9 | "key": 10 | { 11 | "G": 12 | { 13 | "tag": "forge:ingots/gold" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:gold_lock_pick", 19 | "count": 2 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/iron_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " II", 6 | " I ", 7 | "I " 8 | ], 9 | "key": 10 | { 11 | "I": 12 | { 13 | "tag": "forge:ingots/iron" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:iron_lock_pick", 19 | "count": 2 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/steel_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " SS", 6 | " S ", 7 | "S " 8 | ], 9 | "key": 10 | { 11 | "S": 12 | { 13 | "tag": "forge:ingots/steel" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:iron_lock_pick", 19 | "count": 2 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/diamond_lock_pick.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " DD", 6 | " D ", 7 | "D " 8 | ], 9 | "key": 10 | { 11 | "D": 12 | { 13 | "tag": "forge:gems/diamond" 14 | } 15 | }, 16 | "result": 17 | { 18 | "item": "locks:diamond_lock_pick", 19 | "count": 2 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/key_blank.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | "NN", 6 | "NN", 7 | "I " 8 | ], 9 | "key": 10 | { 11 | "N": 12 | { 13 | "tag": "forge:nuggets/iron" 14 | }, 15 | "I": 16 | { 17 | "tag": "forge:ingots/iron" 18 | } 19 | }, 20 | "result": 21 | { 22 | "item": "locks:key_blank" 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/wood_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " W ", 6 | "WLW", 7 | "WWW" 8 | ], 9 | "key": 10 | { 11 | "W": 12 | { 13 | "tag": "minecraft:planks" 14 | }, 15 | "L": 16 | { 17 | "item": "locks:wood_lock_mechanism" 18 | } 19 | }, 20 | "result": 21 | { 22 | "item": "locks:wood_lock" 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/gold_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " G ", 6 | "GLG", 7 | "GGG" 8 | ], 9 | "key": 10 | { 11 | "G": 12 | { 13 | "tag": "forge:ingots/gold" 14 | }, 15 | "L": 16 | { 17 | "item": "locks:steel_lock_mechanism" 18 | } 19 | }, 20 | "result": 21 | { 22 | "item": "locks:gold_lock" 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/iron_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " I ", 6 | "ILI", 7 | "III" 8 | ], 9 | "key": 10 | { 11 | "I": 12 | { 13 | "tag": "forge:ingots/iron" 14 | }, 15 | "L": 16 | { 17 | "item": "locks:iron_lock_mechanism" 18 | } 19 | }, 20 | "result": 21 | { 22 | "item": "locks:iron_lock" 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/steel_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " S ", 6 | "SLS", 7 | "SSS" 8 | ], 9 | "key": 10 | { 11 | "S": 12 | { 13 | "tag": "forge:ingots/steel" 14 | }, 15 | "L": 16 | { 17 | "item": "locks:steel_lock_mechanism" 18 | } 19 | }, 20 | "result": 21 | { 22 | "item": "locks:steel_lock" 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/diamond_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | " D ", 6 | "DLD", 7 | "DDD" 8 | ], 9 | "key": 10 | { 11 | "D": 12 | { 13 | "tag": "forge:gems/diamond" 14 | }, 15 | "L": 16 | { 17 | "item": "locks:steel_lock_mechanism" 18 | } 19 | }, 20 | "result": 21 | { 22 | "item": "locks:diamond_lock" 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/LocksPredicates.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import java.util.function.Predicate; 4 | 5 | public final class LocksPredicates 6 | { 7 | public static final Predicate LOCKED = lkb -> lkb.lock.isLocked(); 8 | 9 | public static final Predicate NOT_LOCKED = lkb -> !lkb.lock.isLocked(); 10 | 11 | private LocksPredicates() {} 12 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/LootPoolAccessor.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.List; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import net.minecraft.loot.LootEntry; 9 | import net.minecraft.loot.LootPool; 10 | 11 | @Mixin(LootPool.class) 12 | public interface LootPoolAccessor 13 | { 14 | @Accessor 15 | List getEntries(); 16 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/LootTableAccessor.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.List; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import net.minecraft.loot.LootPool; 9 | import net.minecraft.loot.LootTable; 10 | 11 | @Mixin(LootTable.class) 12 | public interface LootTableAccessor 13 | { 14 | @Accessor 15 | List getPools(); 16 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/iron_lock_mechanism.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | "SSS", 6 | "NNN", 7 | "III" 8 | ], 9 | "key": 10 | { 11 | "S": 12 | { 13 | "item": "locks:spring" 14 | }, 15 | "N": 16 | { 17 | "tag": "forge:nuggets/iron" 18 | }, 19 | "I": 20 | { 21 | "tag": "forge:ingots/iron" 22 | } 23 | }, 24 | "result": 25 | { 26 | "item": "locks:iron_lock_mechanism" 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/wood_lock_mechanism.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | "SSS", 6 | "TTT", 7 | "WWW" 8 | ], 9 | "key": 10 | { 11 | "S": 12 | { 13 | "tag": "forge:string" 14 | }, 15 | "T": 16 | { 17 | "item": "minecraft:stick" 18 | }, 19 | "W": 20 | { 21 | "tag": "minecraft:planks" 22 | } 23 | }, 24 | "result": 25 | { 26 | "item": "locks:wood_lock_mechanism" 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/recipes/steel_lock_mechanism.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": 4 | [ 5 | "SSS", 6 | "NNN", 7 | "III" 8 | ], 9 | "key": 10 | { 11 | "S": 12 | { 13 | "item": "locks:spring" 14 | }, 15 | "N": 16 | { 17 | "tag": "forge:nuggets/steel" 18 | }, 19 | "I": 20 | { 21 | "tag": "forge:ingots/steel" 22 | } 23 | }, 24 | "result": 25 | { 26 | "item": "locks:steel_lock_mechanism" 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksConfiguredPlacements.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import net.minecraft.world.gen.placement.ConfiguredPlacement; 4 | import net.minecraft.world.gen.placement.IPlacementConfig; 5 | 6 | public class LocksConfiguredPlacements 7 | { 8 | public static final ConfiguredPlacement CONFIGURED_CHEST = LocksPlacements.CHEST.get().configured(IPlacementConfig.NONE); 9 | 10 | private LocksConfiguredPlacements() {} 11 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ForgeHooksAccessor.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.Deque; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import net.minecraftforge.common.ForgeHooks; 9 | 10 | @Mixin(ForgeHooks.class) 11 | public interface ForgeHooksAccessor 12 | { 13 | @Accessor(remap = false) 14 | public static ThreadLocal getLootContext() 15 | { 16 | return null; 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/ILockableStorage.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 4 | import melonslise.locks.common.util.Lockable; 5 | import net.minecraft.nbt.ListNBT; 6 | import net.minecraftforge.common.util.INBTSerializable; 7 | 8 | public interface ILockableStorage extends INBTSerializable 9 | { 10 | Int2ObjectMap get(); 11 | 12 | void add(Lockable lkb); 13 | 14 | void remove(int id); 15 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/WaitAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import melonslise.locks.client.gui.sprite.Sprite; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | 7 | @OnlyIn(Dist.CLIENT) 8 | public class WaitAction extends TimedAction 9 | { 10 | public static WaitAction ticks(int ticks) 11 | { 12 | return (WaitAction) new WaitAction().time(ticks); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/IAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import java.util.function.BiConsumer; 4 | 5 | import melonslise.locks.client.gui.sprite.Sprite; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.api.distmarker.OnlyIn; 8 | 9 | @OnlyIn(Dist.CLIENT) 10 | public interface IAction 11 | { 12 | boolean isFinished(S sprite); 13 | 14 | void update(S sprite); 15 | 16 | void finish(S sprite); 17 | 18 | IAction then(BiConsumer, S> cb); 19 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/models/item/key_ring.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": 4 | { 5 | "layer0": "locks:item/key_ring_0" 6 | }, 7 | "overrides": 8 | [ 9 | { 10 | "predicate": 11 | { 12 | "locks:keys": 0.1 13 | }, 14 | "model": "locks:item/key_ring_1" 15 | }, 16 | { 17 | "predicate": 18 | { 19 | "locks:keys": 0.21 20 | }, 21 | "model": "locks:item/key_ring_2" 22 | }, 23 | { 24 | "predicate": 25 | { 26 | "locks:keys": 0.32 27 | }, 28 | "model": "locks:item/key_ring_3" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/Selection.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import melonslise.locks.Locks; 4 | import net.minecraft.util.ResourceLocation; 5 | import net.minecraft.util.math.BlockPos; 6 | 7 | public class Selection implements ISelection 8 | { 9 | public static final ResourceLocation ID = new ResourceLocation(Locks.ID, "selection"); 10 | 11 | public BlockPos pos; 12 | 13 | @Override 14 | public BlockPos get() 15 | { 16 | return this.pos; 17 | } 18 | 19 | @Override 20 | public void set(BlockPos pos) 21 | { 22 | this.pos = pos; 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksItemTags.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import net.minecraft.item.Item; 5 | import net.minecraft.tags.ITag; 6 | import net.minecraft.tags.ItemTags; 7 | 8 | public final class LocksItemTags 9 | { 10 | private LocksItemTags() {} 11 | 12 | public static final ITag.INamedTag 13 | KEYS = bind("keys"), 14 | LOCKS = bind("locks"), 15 | LOCK_PICKS = bind("lock_picks"); 16 | 17 | public static ITag.INamedTag bind(String name) 18 | { 19 | return ItemTags.bind(Locks.ID + ":" + name); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/EmptyCapabilityStorage.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import net.minecraft.nbt.INBT; 4 | import net.minecraft.util.Direction; 5 | import net.minecraftforge.common.capabilities.Capability; 6 | import net.minecraftforge.common.capabilities.Capability.IStorage; 7 | 8 | public class EmptyCapabilityStorage implements IStorage 9 | { 10 | @Override 11 | public INBT writeNBT(Capability cap, A inst, Direction side) 12 | { 13 | return null; 14 | } 15 | 16 | @Override 17 | public void readNBT(Capability cap, A inst, Direction side, INBT nbt) 18 | { 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ChunkPrimerMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | import melonslise.locks.common.util.ILockableProvider; 9 | import melonslise.locks.common.util.Lockable; 10 | import net.minecraft.world.chunk.ChunkPrimer; 11 | 12 | @Mixin(ChunkPrimer.class) 13 | public class ChunkPrimerMixin implements ILockableProvider 14 | { 15 | private final List lockableList = new ArrayList<>(); 16 | 17 | @Override 18 | public List getLockables() 19 | { 20 | return this.lockableList; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/config/LocksClientConfig.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.config; 2 | 3 | import net.minecraftforge.common.ForgeConfigSpec; 4 | 5 | public final class LocksClientConfig 6 | { 7 | public static final ForgeConfigSpec SPEC; 8 | 9 | public static final ForgeConfigSpec.BooleanValue DEAF_MODE; 10 | 11 | private LocksClientConfig() {} 12 | 13 | static 14 | { 15 | ForgeConfigSpec.Builder cfg = new ForgeConfigSpec.Builder(); 16 | 17 | DEAF_MODE = cfg 18 | .comment("Display visual feedback when trying to use a locked block for certain hearing impaired individuals") 19 | .define("Deaf Mode", true); 20 | 21 | SPEC = cfg.build(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/ILockableHandler.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import java.util.Observer; 4 | 5 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 6 | import melonslise.locks.common.util.Lockable; 7 | import net.minecraft.nbt.IntNBT; 8 | import net.minecraft.util.math.BlockPos; 9 | import net.minecraftforge.common.util.INBTSerializable; 10 | 11 | public interface ILockableHandler extends INBTSerializable, Observer 12 | { 13 | int nextId(); 14 | 15 | Int2ObjectMap getLoaded(); 16 | 17 | Int2ObjectMap getInChunk(BlockPos pos); 18 | 19 | boolean add(Lockable lkb); 20 | 21 | boolean remove(int id); 22 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/TimedAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import melonslise.locks.client.gui.sprite.Sprite; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | 7 | @OnlyIn(Dist.CLIENT) 8 | public abstract class TimedAction extends SingleCallbackAction 9 | { 10 | public int ticks; 11 | 12 | public TimedAction time(int ticks) 13 | { 14 | this.ticks = ticks; 15 | return this; 16 | } 17 | 18 | @Override 19 | public boolean isFinished(S sprite) 20 | { 21 | return this.ticks == 0; 22 | } 23 | 24 | @Override 25 | public void update(S sprite) 26 | { 27 | --this.ticks; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/SerializableCapabilityProvider.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import net.minecraft.nbt.INBT; 4 | import net.minecraftforge.common.capabilities.Capability; 5 | import net.minecraftforge.common.util.INBTSerializable; 6 | 7 | public class SerializableCapabilityProvider extends CapabilityProvider implements INBTSerializable 8 | { 9 | public SerializableCapabilityProvider(Capability cap, A inst) 10 | { 11 | super(cap, inst); 12 | } 13 | 14 | @Override 15 | public INBT serializeNBT() 16 | { 17 | return this.cap.writeNBT(this.inst, null); 18 | } 19 | 20 | @Override 21 | public void deserializeNBT(INBT nbt) 22 | { 23 | this.cap.readNBT(this.inst, null, nbt); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/init/LocksScreens.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.init; 2 | 3 | import melonslise.locks.client.gui.KeyRingScreen; 4 | import melonslise.locks.client.gui.LockPickingScreen; 5 | import melonslise.locks.common.init.LocksContainerTypes; 6 | import net.minecraft.client.gui.ScreenManager; 7 | import net.minecraftforge.api.distmarker.Dist; 8 | import net.minecraftforge.api.distmarker.OnlyIn; 9 | 10 | @OnlyIn(Dist.CLIENT) 11 | public final class LocksScreens 12 | { 13 | private LocksScreens() {} 14 | 15 | public static void register() 16 | { 17 | ScreenManager.register(LocksContainerTypes.LOCK_PICKING.get(), LockPickingScreen::new); 18 | ScreenManager.register(LocksContainerTypes.KEY_RING.get(), KeyRingScreen::new); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/CapabilityStorage.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import net.minecraft.nbt.INBT; 4 | import net.minecraft.util.Direction; 5 | import net.minecraftforge.common.capabilities.Capability; 6 | import net.minecraftforge.common.capabilities.Capability.IStorage; 7 | import net.minecraftforge.common.util.INBTSerializable; 8 | 9 | public class CapabilityStorage implements IStorage 10 | { 11 | @Override 12 | public INBT writeNBT(Capability cap, A inst, Direction side) 13 | { 14 | return inst.serializeNBT(); 15 | } 16 | 17 | @Override 18 | public void readNBT(Capability cap, A inst, Direction side, INBT nbt) 19 | { 20 | inst.deserializeNBT(nbt); 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/SingleCallbackAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import java.util.function.BiConsumer; 4 | 5 | import melonslise.locks.client.gui.sprite.Sprite; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.api.distmarker.OnlyIn; 8 | 9 | @OnlyIn(Dist.CLIENT) 10 | public abstract class SingleCallbackAction implements IAction 11 | { 12 | public BiConsumer, S> cb; 13 | 14 | @Override 15 | public void finish(S sprite) 16 | { 17 | if(this.cb != null) 18 | this.cb.accept(this, sprite); 19 | } 20 | 21 | @Override 22 | public IAction then(BiConsumer, S> cb) 23 | { 24 | this.cb = cb; 25 | return this; 26 | } 27 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![](http://cf.way2muchnoise.eu/full_locks_downloads.svg)](https://minecraft.curseforge.com/projects/locks) [![](http://cf.way2muchnoise.eu/versions/locks.svg)](https://minecraft.curseforge.com/projects/locks) 2 | ![](http://i.imgur.com/M8Np4IB.png) 3 | 4 | The source code for Locks, a small, but unique Minecraft mod that introduces flexible and universal locks which can be dynamically attached to multiple blocks of any kind, including other mods, a fun lock picking mechanic as well as lots of other little, but useful tools and utilities. 5 | 6 | ## Links 7 | CurseForge: https://minecraft.curseforge.com/projects/locks 8 | Minecraft Forum: https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/2759819-locks-v2-0-dynamic-locking-system-lock-picking 9 | -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/enchantment/SturdyEnchantment.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.enchantment; 2 | 3 | import melonslise.locks.common.init.LocksEnchantments; 4 | import net.minecraft.enchantment.Enchantment; 5 | import net.minecraft.inventory.EquipmentSlotType; 6 | 7 | public class SturdyEnchantment extends Enchantment 8 | { 9 | public SturdyEnchantment() 10 | { 11 | super(Rarity.RARE, LocksEnchantments.LOCK_TYPE, new EquipmentSlotType[] { EquipmentSlotType.MAINHAND }); 12 | } 13 | 14 | @Override 15 | public int getMinCost(int level) 16 | { 17 | return 5 + (level - 1) * 15; 18 | } 19 | 20 | @Override 21 | public int getMaxCost(int level) 22 | { 23 | return 50; 24 | } 25 | 26 | @Override 27 | public int getMaxLevel() 28 | { 29 | return 3; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/enchantment/ComplexityEnchantment.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.enchantment; 2 | 3 | import melonslise.locks.common.init.LocksEnchantments; 4 | import net.minecraft.enchantment.Enchantment; 5 | import net.minecraft.inventory.EquipmentSlotType; 6 | 7 | public class ComplexityEnchantment extends Enchantment 8 | { 9 | public ComplexityEnchantment() 10 | { 11 | super(Rarity.VERY_RARE, LocksEnchantments.LOCK_TYPE, new EquipmentSlotType[] { EquipmentSlotType.MAINHAND }); 12 | } 13 | 14 | @Override 15 | public int getMinCost(int level) 16 | { 17 | return 7 + level * 10; 18 | } 19 | 20 | @Override 21 | public int getMaxCost(int level) 22 | { 23 | return this.getMinCost(level) + 15; 24 | } 25 | 26 | @Override 27 | public int getMaxLevel() 28 | { 29 | return 3; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/enchantment/ShockingEnchantment.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.enchantment; 2 | 3 | import melonslise.locks.common.init.LocksEnchantments; 4 | import net.minecraft.enchantment.Enchantment; 5 | import net.minecraft.inventory.EquipmentSlotType; 6 | 7 | public class ShockingEnchantment extends Enchantment 8 | { 9 | public ShockingEnchantment() 10 | { 11 | super(Rarity.UNCOMMON, LocksEnchantments.LOCK_TYPE, new EquipmentSlotType[] { EquipmentSlotType.MAINHAND }); 12 | } 13 | 14 | @Override 15 | public int getMinCost(int level) 16 | { 17 | return 2 + (level - 1) * 9; 18 | } 19 | 20 | @Override 21 | public int getMaxCost(int level) 22 | { 23 | return this.getMinCost(level) + 30; 24 | } 25 | 26 | @Override 27 | public int getMaxLevel() 28 | { 29 | return 5; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/WorldMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.World; 11 | 12 | @Mixin(World.class) 13 | public class WorldMixin 14 | { 15 | @Inject(at = @At("HEAD"), method = "hasNeighborSignal(Lnet/minecraft/util/math/BlockPos;)Z", cancellable = true) 16 | private void hasNeighborSignal(BlockPos pos, CallbackInfoReturnable cir) 17 | { 18 | if(LocksUtil.locked((World) (Object) this, pos)) 19 | cir.setReturnValue(false); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/event/LocksClientModEvents.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.event; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.client.init.LocksItemModelsProperties; 5 | import melonslise.locks.client.init.LocksScreens; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.eventbus.api.SubscribeEvent; 8 | import net.minecraftforge.fml.common.Mod; 9 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; 10 | 11 | @Mod.EventBusSubscriber(modid = Locks.ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) 12 | public final class LocksClientModEvents 13 | { 14 | private LocksClientModEvents() {} 15 | 16 | @SubscribeEvent 17 | public static void onSetup(FMLClientSetupEvent e) 18 | { 19 | LocksScreens.register(); 20 | LocksItemModelsProperties.register(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/PistonBlockStructureHelperMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.block.PistonBlockStructureHelper; 10 | 11 | @Mixin(PistonBlockStructureHelper.class) 12 | public class PistonBlockStructureHelperMixin 13 | { 14 | @Inject(at = @At("HEAD"), method = "resolve()Z", cancellable = true) 15 | private void resolve(CallbackInfoReturnable cir) 16 | { 17 | PistonBlockStructureHelper h = (PistonBlockStructureHelper) (Object) this; 18 | if(LocksUtil.locked(h.level, h.startPos)) 19 | cir.setReturnValue(false); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/resources/locks.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "melonslise.locks.mixin", 4 | "compatibilityLevel": "JAVA_8", 5 | "refmap": "locks.refmap.json", 6 | "mixins": 7 | [ 8 | "AbstractFurnaceTileEntityMixin", 9 | "ChestBlockMixin", 10 | "ChestTileEntityMixin", 11 | "ChunkManagerMixin", 12 | "ChunkMixin", 13 | "ChunkPrimerMixin", 14 | "ExplosionContextMixin", 15 | "ForgeHooksAccessor", 16 | "HopperTileEntityMixin", 17 | "LockableTileEntityMixin", 18 | "LootPoolAccessor", 19 | "LootTableAccessor", 20 | "LootTableManagerMixin", 21 | "PistonBlockMixin", 22 | "PistonBlockStructureHelperMixin", 23 | "ServerWorldMixin", 24 | "TemplateMixin", 25 | "WorldMixin" 26 | ], 27 | "client": 28 | [ 29 | "client.WorldRendererMixin" 30 | ], 31 | "injectors": 32 | { 33 | "defaultRequire": 1 34 | }, 35 | "minVersion": "0.8" 36 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/CapabilityProvider.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import net.minecraft.util.Direction; 4 | import net.minecraftforge.common.capabilities.Capability; 5 | import net.minecraftforge.common.capabilities.ICapabilityProvider; 6 | import net.minecraftforge.common.util.LazyOptional; 7 | 8 | public class CapabilityProvider implements ICapabilityProvider 9 | { 10 | public final Capability cap; 11 | public final A inst; 12 | public final LazyOptional opt; 13 | 14 | public CapabilityProvider(Capability cap, A inst) 15 | { 16 | this.cap = cap; 17 | this.inst = inst; 18 | this.opt = LazyOptional.of(() -> inst); 19 | } 20 | 21 | @Override 22 | public LazyOptional getCapability(Capability cap, Direction side) 23 | { 24 | return cap == this.cap ? this.opt.cast() : LazyOptional.empty(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.client.renderer.culling.ClippingHelper func_228953_a_(DDDDDD)Z # cubeInFrustum 2 | public net.minecraft.client.renderer.WorldRenderer field_175001_U # capturedFrustum 3 | public net.minecraft.client.renderer.GameRenderer func_215311_a(Lnet/minecraft/client/renderer/ActiveRenderInfo;FZ)D # getFov 4 | public net.minecraft.world.gen.feature.WorldDecoratingHelper field_242889_a # level 5 | public net.minecraft.loot.LootTableManager field_186526_b # GSON 6 | public net.minecraft.entity.merchant.villager.VillagerTrades$ItemsForEmeraldsTrade 7 | public net.minecraft.entity.merchant.villager.VillagerTrades$EnchantedItemForEmeraldsTrade 8 | public net.minecraft.block.PistonBlockStructureHelper field_177261_a # level 9 | public net.minecraft.block.PistonBlockStructureHelper field_177260_c # startPos 10 | public net.minecraft.world.Explosion field_77287_j # level -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/SpringSprite.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite; 2 | 3 | import com.mojang.blaze3d.matrix.MatrixStack; 4 | 5 | import melonslise.locks.client.util.LocksClientUtil; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.api.distmarker.OnlyIn; 8 | 9 | @OnlyIn(Dist.CLIENT) 10 | public class SpringSprite extends Sprite 11 | { 12 | public final TextureInfo[] texs; 13 | public Sprite target; 14 | 15 | public SpringSprite(TextureInfo[] texs, Sprite target) 16 | { 17 | super(texs[0]); 18 | this.texs = texs; 19 | this.target = target; 20 | } 21 | 22 | @Override 23 | public void draw(MatrixStack mtx, float partialTick) 24 | { 25 | for(TextureInfo tex : this.texs) 26 | if(LocksClientUtil.lerp(this.target.oldPosY, this.target.posY, partialTick) < this.posY + tex.height) 27 | this.tex = tex; 28 | super.draw(mtx, partialTick); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/TextureInfo.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite; 2 | 3 | import com.mojang.blaze3d.matrix.MatrixStack; 4 | 5 | import melonslise.locks.client.util.LocksClientUtil; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.api.distmarker.OnlyIn; 8 | 9 | @OnlyIn(Dist.CLIENT) 10 | public class TextureInfo 11 | { 12 | public int startX, startY, width, height, canvasWidth, canvasHeight; 13 | 14 | public TextureInfo(int startX, int startY, int width, int height, int canvasWidth, int canvasHeight) 15 | { 16 | this.startX = startX; 17 | this.startY = startY; 18 | this.width = width; 19 | this.height = height; 20 | this.canvasWidth = canvasWidth; 21 | this.canvasHeight = canvasHeight; 22 | } 23 | 24 | public void draw(MatrixStack mtx, float x, float y, float alpha) 25 | { 26 | LocksClientUtil.texture(mtx, x, y, this.startX, this.startY, this.width, this.height, this.canvasWidth, this.canvasHeight, alpha); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/HopperTileEntityMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.inventory.IInventory; 10 | import net.minecraft.tileentity.HopperTileEntity; 11 | import net.minecraft.util.math.BlockPos; 12 | import net.minecraft.world.World; 13 | 14 | @Mixin(HopperTileEntity.class) 15 | public class HopperTileEntityMixin 16 | { 17 | @Inject(at = @At("HEAD"), method = "getContainerAt(Lnet/minecraft/world/World;DDD)Lnet/minecraft/inventory/IInventory;", cancellable = true) 18 | private static void getContainerAt(World world, double x, double y, double z, CallbackInfoReturnable cir) 19 | { 20 | if(LocksUtil.locked(world, new BlockPos(x, y, z))) 21 | cir.setReturnValue(null); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksPacketDistributors.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import java.util.Arrays; 4 | import java.util.stream.Stream; 5 | 6 | import net.minecraft.world.chunk.Chunk; 7 | import net.minecraft.world.server.ServerChunkProvider; 8 | import net.minecraftforge.fml.network.NetworkDirection; 9 | import net.minecraftforge.fml.network.PacketDistributor; 10 | 11 | public final class LocksPacketDistributors 12 | { 13 | public static final PacketDistributor> TRACKING_AREA = new PacketDistributor<>((pd, s) -> 14 | pkt -> 15 | { 16 | // Convert each chunk to a stream of tracking players 17 | // Merge all streams into one 18 | // Remove duplicate players 19 | // Send packet 20 | s.get() 21 | .flatMap(chunk -> ((ServerChunkProvider) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false)) 22 | .distinct() 23 | .forEach(p -> p.connection.send(pkt)); 24 | }, NetworkDirection.PLAY_TO_CLIENT); 25 | 26 | private LocksPacketDistributors() {} 27 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/LootTableManagerMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.Map; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | import com.google.gson.JsonElement; 11 | 12 | import melonslise.locks.common.util.LocksUtil; 13 | import net.minecraft.loot.LootTableManager; 14 | import net.minecraft.profiler.IProfiler; 15 | import net.minecraft.resources.IResourceManager; 16 | import net.minecraft.util.ResourceLocation; 17 | 18 | @Mixin(LootTableManager.class) 19 | public class LootTableManagerMixin 20 | { 21 | @Inject(at = @At("HEAD"), method = "apply(Ljava/util/Map;Lnet/minecraft/resources/IResourceManager;Lnet/minecraft/profiler/IProfiler;)V") 22 | private void apply(Map map, IResourceManager mgr, IProfiler p, CallbackInfo ci) 23 | { 24 | LocksUtil.resourceManager = mgr; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/FadeAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import melonslise.locks.client.gui.sprite.Sprite; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | 7 | @OnlyIn(Dist.CLIENT) 8 | public class FadeAction extends TimedAction 9 | { 10 | public float fadeSpeed; 11 | 12 | private FadeAction(float fadeSpeed) 13 | { 14 | this.fadeSpeed = fadeSpeed; 15 | } 16 | 17 | public static FadeAction at(float fadeSpeed) 18 | { 19 | return new FadeAction(fadeSpeed); 20 | } 21 | 22 | public static FadeAction to(float delta, int ticks) 23 | { 24 | return (FadeAction) at(delta / ticks).time(ticks); 25 | } 26 | 27 | public static FadeAction to(Sprite sprite, float alpha, int ticks) 28 | { 29 | return to(alpha - sprite.alpha, ticks); 30 | } 31 | 32 | @Override 33 | public void update(S sprite) 34 | { 35 | super.update(sprite); 36 | sprite.alpha += this.fadeSpeed; 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksConfiguredFeatures.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import net.minecraft.util.registry.Registry; 4 | import net.minecraft.util.registry.WorldGenRegistries; 5 | import net.minecraft.world.gen.GenerationStage; 6 | import net.minecraft.world.gen.feature.ConfiguredFeature; 7 | import net.minecraft.world.gen.feature.IFeatureConfig; 8 | import net.minecraftforge.event.world.BiomeLoadingEvent; 9 | 10 | public final class LocksConfiguredFeatures 11 | { 12 | public static final ConfiguredFeature CONFIGURED_LOCK_CHESTS = add("lock_chests", LocksFeatures.LOCK_CHESTS.get().configured(IFeatureConfig.NONE).decorated(LocksConfiguredPlacements.CONFIGURED_CHEST)); 13 | 14 | private LocksConfiguredFeatures() {} 15 | 16 | public static void addTo(BiomeLoadingEvent e) 17 | { 18 | e.getGeneration().addFeature(GenerationStage.Decoration.TOP_LAYER_MODIFICATION, LocksConfiguredFeatures.CONFIGURED_LOCK_CHESTS); 19 | } 20 | 21 | public static ConfiguredFeature add(String name, ConfiguredFeature cf) 22 | { 23 | return Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, name, cf); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[36,)" 3 | license="Attribution-NonCommercial 3.0 Unported" 4 | issueTrackerURL="https://github.com/Melonslise/Locks/issues" 5 | 6 | [[mods]] 7 | modId="locks" 8 | version="3.0.3" 9 | displayName="Locks" 10 | #updateJSONURL="https://change.me.example.invalid/updates.json" 11 | displayURL="https://minecraft.curseforge.com/projects/locks" 12 | logoFile="logo.png" 13 | credits="Huge thanks for the new textures to the following artists on the RLCraft server: Hoonts, Artsy (ydgy); for coding help on MMD: Diesieben07, Choonster, Gigaherz, Draco18s, JamiesWhiteShirt, Shadows, DShadowWolf, Xalcon, DarkCow, KillJoy, Ben, TheRealFarfetchd, JTK222, Cadiboo, Lykrast, Quadraxis, Tterrag, Alexbegt, Desht, McJty, AlcatrazEscapee, V0idWa1k3r, Muramasa, Kashike, Commoble, TheSilkMiner, and others; for sounds: freesound.org" 14 | authors="Melonslise" 15 | description=''' 16 | Locks is a small, but unique Minecraft mod that introduces flexible and universal locks which can be dynamically attached to multiple blocks of any kind, including other mods, a fun lock picking mechanic as well as lots of other little, but useful tools and utilities. 17 | ''' -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/event/LocksModEvents.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.event; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.config.LocksConfig; 5 | import melonslise.locks.common.config.LocksServerConfig; 6 | import melonslise.locks.common.init.LocksCapabilities; 7 | import melonslise.locks.common.init.LocksNetwork; 8 | import net.minecraftforge.eventbus.api.SubscribeEvent; 9 | import net.minecraftforge.fml.common.Mod; 10 | import net.minecraftforge.fml.config.ModConfig; 11 | import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; 12 | 13 | @Mod.EventBusSubscriber(modid = Locks.ID, bus = Mod.EventBusSubscriber.Bus.MOD) 14 | public final class LocksModEvents 15 | { 16 | private LocksModEvents() {} 17 | 18 | @SubscribeEvent 19 | public static void onSetup(FMLCommonSetupEvent e) 20 | { 21 | LocksCapabilities.register(); 22 | LocksNetwork.register(); 23 | } 24 | 25 | @SubscribeEvent 26 | public static void onConfigLoad(ModConfig.ModConfigEvent e) 27 | { 28 | if(e.getConfig().getSpec() == LocksConfig.SPEC) 29 | LocksConfig.init(); 30 | if(e.getConfig().getSpec() == LocksServerConfig.SPEC) 31 | LocksServerConfig.init(); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/MoveAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import melonslise.locks.client.gui.sprite.Sprite; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | 7 | @OnlyIn(Dist.CLIENT) 8 | public class MoveAction extends TimedAction 9 | { 10 | public float speedX, speedY; 11 | 12 | public MoveAction(float speedX, float speedY) 13 | { 14 | this.speedX = speedX; 15 | this.speedY = speedY; 16 | } 17 | 18 | public static MoveAction at(float speedX, float speedY) 19 | { 20 | return new MoveAction(speedX, speedY); 21 | } 22 | 23 | public static MoveAction to(float shiftX, float shiftY, int ticks) 24 | { 25 | return (MoveAction) at(shiftX / ticks, shiftY / ticks).time(ticks); 26 | } 27 | 28 | public static MoveAction to(Sprite sprite, float posX, float posY, int ticks) 29 | { 30 | return to(posX - sprite.posX, posY - sprite.posY, ticks); 31 | } 32 | 33 | @Override 34 | public void update(S sprite) 35 | { 36 | super.update(sprite); 37 | sprite.posX += this.speedX; 38 | sprite.posY += this.speedY; 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/worldgen/ChestPlacement.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.worldgen; 2 | 3 | import java.util.Random; 4 | import java.util.stream.Stream; 5 | 6 | import com.mojang.serialization.Codec; 7 | 8 | import net.minecraft.block.BlockState; 9 | import net.minecraft.block.ChestBlock; 10 | import net.minecraft.state.properties.ChestType; 11 | import net.minecraft.util.math.BlockPos; 12 | import net.minecraft.world.gen.feature.WorldDecoratingHelper; 13 | import net.minecraft.world.gen.placement.NoPlacementConfig; 14 | import net.minecraft.world.gen.placement.Placement; 15 | 16 | public class ChestPlacement extends Placement 17 | { 18 | public ChestPlacement(Codec codec) 19 | { 20 | super(codec); 21 | } 22 | 23 | @Override 24 | public Stream getPositions(WorldDecoratingHelper helper, Random rng, NoPlacementConfig cfg, BlockPos pos) 25 | { 26 | return helper.level.getChunk(pos).getBlockEntitiesPos().stream() 27 | .filter(tePos -> 28 | { 29 | BlockState state = helper.level.getBlockState(tePos); 30 | // Prevent from adding double chests twice 31 | return state.hasProperty(ChestBlock.TYPE) && state.getValue(ChestBlock.TYPE) != ChestType.RIGHT; 32 | }); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/network/toclient/RemoveLockablePacket.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.network.toclient; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import melonslise.locks.common.init.LocksCapabilities; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.network.PacketBuffer; 8 | import net.minecraftforge.fml.network.NetworkEvent; 9 | 10 | public class RemoveLockablePacket 11 | { 12 | private final int id; 13 | 14 | public RemoveLockablePacket(int id) 15 | { 16 | this.id = id; 17 | } 18 | 19 | public static RemoveLockablePacket decode(PacketBuffer buf) 20 | { 21 | return new RemoveLockablePacket(buf.readInt()); 22 | } 23 | 24 | public static void encode(RemoveLockablePacket pkt, PacketBuffer buf) 25 | { 26 | buf.writeInt(pkt.id); 27 | } 28 | 29 | public static void handle(RemoveLockablePacket pkt, Supplier ctx) 30 | { 31 | // Use runnable, lambda causes issues with class loading 32 | ctx.get().enqueueWork(new Runnable() 33 | { 34 | @Override 35 | public void run() 36 | { 37 | Minecraft.getInstance().level.getCapability(LocksCapabilities.LOCKABLE_HANDLER).ifPresent(handler -> handler.remove(pkt.id)); 38 | } 39 | }); 40 | ctx.get().setPacketHandled(true); 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksFeatures.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.worldgen.LockChestsFeature; 5 | import net.minecraft.world.gen.feature.Feature; 6 | import net.minecraft.world.gen.feature.IFeatureConfig; 7 | import net.minecraft.world.gen.feature.NoFeatureConfig; 8 | import net.minecraftforge.fml.RegistryObject; 9 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | public final class LocksFeatures 14 | { 15 | public static final DeferredRegister> FEATURES = DeferredRegister.create(ForgeRegistries.FEATURES, Locks.ID); 16 | 17 | public static final RegistryObject> 18 | LOCK_CHESTS = add("lock_chests", new LockChestsFeature(NoFeatureConfig.CODEC)); 19 | 20 | private LocksFeatures() {} 21 | 22 | public static void register() 23 | { 24 | FEATURES.register(FMLJavaModLoadingContext.get().getModEventBus()); 25 | } 26 | 27 | public static RegistryObject> add(String name, Feature feature) 28 | { 29 | return FEATURES.register(name, () -> feature); 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksPlacements.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.worldgen.ChestPlacement; 5 | import net.minecraft.world.gen.placement.IPlacementConfig; 6 | import net.minecraft.world.gen.placement.NoPlacementConfig; 7 | import net.minecraft.world.gen.placement.Placement; 8 | import net.minecraftforge.fml.RegistryObject; 9 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | public final class LocksPlacements 14 | { 15 | public static final DeferredRegister> PLACEMENTS = DeferredRegister.create(ForgeRegistries.DECORATORS, Locks.ID); 16 | 17 | public static final RegistryObject> 18 | CHEST = add("chest", new ChestPlacement(NoPlacementConfig.CODEC)); 19 | 20 | private LocksPlacements() {} 21 | 22 | public static void register() 23 | { 24 | PLACEMENTS.register(FMLJavaModLoadingContext.get().getModEventBus()); 25 | } 26 | 27 | public static RegistryObject> add(String name, Placement pl) 28 | { 29 | return PLACEMENTS.register(name, () -> pl); 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/underwater_ruin_small.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 2.0, 10 | "max": 8.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:gold_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 3.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:iron_lock_pick", 35 | "weight": 3, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 1.0, 43 | "max": 3.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | } 49 | ] 50 | }, 51 | { 52 | "name": "pool1", 53 | "rolls": 1, 54 | "entries": 55 | [ 56 | { 57 | "type": "minecraft:item", 58 | "name": "locks:iron_lock_mechanism", 59 | "weight": 3 60 | } 61 | ] 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksSoundEvents.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import net.minecraft.util.ResourceLocation; 5 | import net.minecraft.util.SoundEvent; 6 | import net.minecraftforge.fml.RegistryObject; 7 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 8 | import net.minecraftforge.registries.DeferredRegister; 9 | import net.minecraftforge.registries.ForgeRegistries; 10 | 11 | public final class LocksSoundEvents 12 | { 13 | public static final DeferredRegister SOUND_EVENTS = DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, Locks.ID); 14 | 15 | public static final RegistryObject 16 | KEY_RING = add("key_ring"), 17 | LOCK_CLOSE = add("lock.close"), 18 | LOCK_OPEN = add("lock.open"), 19 | LOCK_RATTLE = add("lock.rattle"), 20 | PIN_FAIL = add("pin.fail"), 21 | PIN_MATCH = add("pin.match"), 22 | SHOCK = add("shock"); 23 | 24 | private LocksSoundEvents() {} 25 | 26 | public static void register() 27 | { 28 | SOUND_EVENTS.register(FMLJavaModLoadingContext.get().getModEventBus()); 29 | } 30 | 31 | public static RegistryObject add(String name) 32 | { 33 | return SOUND_EVENTS.register(name, () -> new SoundEvent(new ResourceLocation(Locks.ID, name))); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/PistonBlockMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.block.BlockState; 10 | import net.minecraft.block.PistonBlock; 11 | import net.minecraft.util.Direction; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.world.World; 14 | 15 | @Mixin(PistonBlock.class) 16 | public class PistonBlockMixin 17 | { 18 | // Before getPistonPushReaction call 19 | @Inject(at = @At(value = "INVOKE", target = "net/minecraft/block/BlockState.getPistonPushReaction()Lnet/minecraft/block/material/PushReaction;", ordinal = 0), method = "isPushable(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;ZLnet/minecraft/util/Direction;)Z", cancellable = true) 20 | private static void isPushable(BlockState state, World world, BlockPos pos, Direction dir, boolean flag, Direction dir1, CallbackInfoReturnable cir) 21 | { 22 | if(LocksUtil.locked(world, pos)) 23 | cir.setReturnValue(false); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/network/toclient/AddLockablePacket.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.network.toclient; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import melonslise.locks.common.init.LocksCapabilities; 6 | import melonslise.locks.common.util.Lockable; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.network.PacketBuffer; 9 | import net.minecraftforge.fml.network.NetworkEvent; 10 | 11 | public class AddLockablePacket 12 | { 13 | private final Lockable lockable; 14 | 15 | public AddLockablePacket(Lockable lkb) 16 | { 17 | this.lockable = lkb; 18 | } 19 | 20 | public static AddLockablePacket decode(PacketBuffer buf) 21 | { 22 | return new AddLockablePacket(Lockable.fromBuf(buf)); 23 | } 24 | 25 | public static void encode(AddLockablePacket pkt, PacketBuffer buf) 26 | { 27 | Lockable.toBuf(buf, pkt.lockable); 28 | } 29 | 30 | public static void handle(AddLockablePacket pkt, Supplier ctx) 31 | { 32 | // Use runnable, lambda causes issues with class loading 33 | ctx.get().enqueueWork(new Runnable() 34 | { 35 | @Override 36 | public void run() 37 | { 38 | Minecraft.getInstance().level.getCapability(LocksCapabilities.LOCKABLE_HANDLER).ifPresent(handler -> handler.add(pkt.lockable)); 39 | } 40 | }); 41 | ctx.get().setPacketHandled(true); 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksRecipeSerializers.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.recipe.KeyRecipe; 5 | import net.minecraft.item.crafting.IRecipe; 6 | import net.minecraft.item.crafting.IRecipeSerializer; 7 | import net.minecraft.item.crafting.SpecialRecipeSerializer; 8 | import net.minecraftforge.fml.RegistryObject; 9 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | public final class LocksRecipeSerializers 14 | { 15 | public static final DeferredRegister> RECIPE_SERIALIZERS = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, Locks.ID); 16 | 17 | public static final RegistryObject> KEY = add("crafting_key", new SpecialRecipeSerializer<>(KeyRecipe::new)); 18 | 19 | private LocksRecipeSerializers() {} 20 | 21 | public static void register() 22 | { 23 | RECIPE_SERIALIZERS.register(FMLJavaModLoadingContext.get().getModEventBus()); 24 | } 25 | 26 | public static > RegistryObject> add(String name, IRecipeSerializer serializer) 27 | { 28 | return RECIPE_SERIALIZERS.register(name, () -> serializer); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/LockableInfo.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import net.minecraft.item.ItemStack; 4 | import net.minecraft.nbt.CompoundNBT; 5 | 6 | public class LockableInfo 7 | { 8 | public final Cuboid6i bb; 9 | public final Lock lock; 10 | public final Transform tr; 11 | public final ItemStack stack; 12 | public final int id; 13 | 14 | public LockableInfo(Cuboid6i bb, Lock lock, Transform tr, ItemStack stack, int id) 15 | { 16 | this.bb = bb; 17 | this.lock = lock; 18 | this.tr = tr; 19 | this.stack = stack; 20 | this.id = id; 21 | } 22 | 23 | public static LockableInfo fromNbt(CompoundNBT nbt) 24 | { 25 | return new LockableInfo(Cuboid6i.fromNbt(nbt.getCompound(Lockable.KEY_BB)), Lock.fromNbt(nbt.getCompound(Lockable.KEY_LOCK)), Transform.values()[(int) nbt.getByte(Lockable.KEY_TRANSFORM)], ItemStack.of(nbt.getCompound(Lockable.KEY_STACK)), nbt.getInt(Lockable.KEY_ID)); 26 | } 27 | 28 | public static CompoundNBT toNbt(LockableInfo lkb) 29 | { 30 | CompoundNBT nbt = new CompoundNBT(); 31 | nbt.put(Lockable.KEY_BB, Cuboid6i.toNbt(lkb.bb)); 32 | nbt.put(Lockable.KEY_LOCK, Lock.toNbt(lkb.lock)); 33 | nbt.putByte(Lockable.KEY_TRANSFORM, (byte) lkb.tr.ordinal()); 34 | nbt.put(Lockable.KEY_STACK, lkb.stack.serializeNBT()); 35 | nbt.putInt(Lockable.KEY_ID, lkb.id); 36 | return nbt; 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ChunkManagerMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import melonslise.locks.common.init.LocksCapabilities; 9 | import melonslise.locks.common.init.LocksNetwork; 10 | import melonslise.locks.common.network.toclient.AddLockableToChunkPacket; 11 | import net.minecraft.entity.player.ServerPlayerEntity; 12 | import net.minecraft.network.IPacket; 13 | import net.minecraft.world.chunk.Chunk; 14 | import net.minecraft.world.server.ChunkManager; 15 | import net.minecraftforge.fml.network.PacketDistributor; 16 | 17 | @Mixin(ChunkManager.class) 18 | public class ChunkManagerMixin 19 | { 20 | @Inject(at = @At("TAIL"), method = "playerLoadedChunk(Lnet/minecraft/entity/player/ServerPlayerEntity;[Lnet/minecraft/network/IPacket;Lnet/minecraft/world/chunk/Chunk;)V") 21 | private void playerLoadedChunk(ServerPlayerEntity player, IPacket[] pkts, Chunk ch, CallbackInfo ci) 22 | { 23 | ch.getCapability(LocksCapabilities.LOCKABLE_STORAGE).orElse(null).get().values() 24 | .forEach(lkb -> LocksNetwork.MAIN.send(PacketDistributor.TRACKING_CHUNK.with(() -> ch), new AddLockableToChunkPacket(lkb, ch))); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ChestTileEntityMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.tileentity.ChestTileEntity; 10 | import net.minecraft.tileentity.TileEntity; 11 | import net.minecraft.util.Direction; 12 | import net.minecraftforge.common.capabilities.Capability; 13 | import net.minecraftforge.common.util.LazyOptional; 14 | import net.minecraftforge.items.CapabilityItemHandler; 15 | 16 | @Mixin(ChestTileEntity.class) 17 | public class ChestTileEntityMixin 18 | { 19 | @Inject(at = @At("HEAD"), method = "getCapability(Lnet/minecraftforge/common/capabilities/Capability;Lnet/minecraft/util/Direction;)Lnet/minecraftforge/common/util/LazyOptional;", cancellable = true, remap = false) 20 | private void getCapability(Capability cap, Direction side, CallbackInfoReturnable cir) 21 | { 22 | TileEntity te = (TileEntity) (Object) this; 23 | if(!te.isRemoved() && cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY && te.hasLevel() && LocksUtil.locked(te.getLevel(), te.getBlockPos())) 24 | cir.setReturnValue(LazyOptional.empty()); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/network/toserver/TryPinPacket.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.network.toserver; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import melonslise.locks.common.container.LockPickingContainer; 6 | import melonslise.locks.common.init.LocksContainerTypes; 7 | import net.minecraft.inventory.container.Container; 8 | import net.minecraft.network.PacketBuffer; 9 | import net.minecraftforge.fml.network.NetworkEvent; 10 | 11 | public class TryPinPacket 12 | { 13 | private final byte pin; 14 | 15 | public TryPinPacket(byte pin) 16 | { 17 | this.pin = pin; 18 | } 19 | 20 | public static TryPinPacket decode(PacketBuffer buf) 21 | { 22 | return new TryPinPacket(buf.readByte()); 23 | } 24 | 25 | public static void encode(TryPinPacket pkt, PacketBuffer buf) 26 | { 27 | buf.writeByte(pkt.pin); 28 | } 29 | 30 | public static void handle(TryPinPacket pkt, Supplier ctx) 31 | { 32 | // Use runnable, lambda causes issues with class loading 33 | ctx.get().enqueueWork(new Runnable() 34 | { 35 | @Override 36 | public void run() 37 | { 38 | Container container = ctx.get().getSender().containerMenu; 39 | if(container.getType() == LocksContainerTypes.LOCK_PICKING.get()) 40 | ((LockPickingContainer) container).tryPin(pkt.pin); 41 | } 42 | }); 43 | ctx.get().setPacketHandled(true); 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/item/MasterKeyItem.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.item; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | import melonslise.locks.common.init.LocksSoundEvents; 7 | import melonslise.locks.common.util.Lockable; 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.item.ItemUseContext; 11 | import net.minecraft.util.ActionResultType; 12 | import net.minecraft.util.SoundCategory; 13 | import net.minecraft.util.math.BlockPos; 14 | import net.minecraft.world.World; 15 | 16 | public class MasterKeyItem extends Item 17 | { 18 | public MasterKeyItem(Properties props) 19 | { 20 | super(props.stacksTo(1)); 21 | } 22 | 23 | // TODO Sound pitch 24 | @Override 25 | public ActionResultType useOn(ItemUseContext ctx) 26 | { 27 | World world = ctx.getLevel(); 28 | BlockPos pos = ctx.getClickedPos(); 29 | List match = LocksUtil.intersecting(world, pos).collect(Collectors.toList()); 30 | if(match.isEmpty()) 31 | return ActionResultType.PASS; 32 | world.playSound(ctx.getPlayer(), pos, LocksSoundEvents.LOCK_OPEN.get(), SoundCategory.BLOCKS, 1f, 1f); 33 | if(world.isClientSide) 34 | return ActionResultType.SUCCESS; 35 | for(Lockable lkb : match) 36 | lkb.lock.setLocked(!lkb.lock.isLocked()); 37 | return ActionResultType.SUCCESS; 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/LockableTileEntityMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.tileentity.LockableTileEntity; 10 | import net.minecraft.tileentity.TileEntity; 11 | import net.minecraft.util.Direction; 12 | import net.minecraftforge.common.capabilities.Capability; 13 | import net.minecraftforge.common.util.LazyOptional; 14 | import net.minecraftforge.items.CapabilityItemHandler; 15 | 16 | @Mixin(LockableTileEntity.class) 17 | public class LockableTileEntityMixin 18 | { 19 | @Inject(at = @At("HEAD"), method = "getCapability(Lnet/minecraftforge/common/capabilities/Capability;Lnet/minecraft/util/Direction;)Lnet/minecraftforge/common/util/LazyOptional;", cancellable = true, remap = false) 20 | private void getCapability(Capability cap, Direction side, CallbackInfoReturnable cir) 21 | { 22 | TileEntity te = (TileEntity) (Object) this; 23 | if(!te.isRemoved() && cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY && te.hasLevel() && LocksUtil.locked(te.getLevel(), te.getBlockPos())) 24 | cir.setReturnValue(LazyOptional.empty()); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_ring": 3 | { 4 | "subtitle": "locks.subtitle.key_ring", 5 | "sounds": 6 | [ 7 | { 8 | "name": "locks:key_ring1" 9 | }, 10 | { 11 | "name": "locks:key_ring2" 12 | } 13 | ] 14 | }, 15 | "lock.close": 16 | { 17 | "subtitle": "locks.subtitle.lock.close", 18 | "sounds": 19 | [ 20 | { 21 | "name": "locks:lock.close1" 22 | }, 23 | { 24 | "name": "locks:lock.close2" 25 | } 26 | ] 27 | }, 28 | "lock.open": 29 | { 30 | "subtitle": "locks.subtitle.lock.open", 31 | "sounds": 32 | [ 33 | { 34 | "name": "locks:lock.open" 35 | } 36 | ] 37 | }, 38 | "lock.rattle": 39 | { 40 | "subtitle": "locks.subtitle.lock.rattle", 41 | "sounds": 42 | [ 43 | { 44 | "name": "locks:lock.rattle" 45 | } 46 | ] 47 | }, 48 | "pin.fail": 49 | { 50 | "subtitle": "locks.subtitle.pin.fail", 51 | "sounds": 52 | [ 53 | { 54 | "name": "locks:pin.fail" 55 | } 56 | ] 57 | }, 58 | "pin.match": 59 | { 60 | "subtitle": "locks.subtitle.pin.match", 61 | "sounds": 62 | [ 63 | { 64 | "name": "locks:pin.match1" 65 | }, 66 | { 67 | "name": "locks:pin.match2" 68 | } 69 | ] 70 | }, 71 | "shock": 72 | { 73 | "subtitle": "locks.subtitle.shock", 74 | "sounds": 75 | [ 76 | { 77 | "name": "locks:shock1" 78 | } 79 | ] 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/item/KeyItem.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.item; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | import melonslise.locks.common.init.LocksSoundEvents; 7 | import melonslise.locks.common.util.Lockable; 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.item.ItemUseContext; 10 | import net.minecraft.util.ActionResultType; 11 | import net.minecraft.util.SoundCategory; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.world.World; 14 | 15 | public class KeyItem extends LockingItem 16 | { 17 | public KeyItem(Properties props) 18 | { 19 | super(props); 20 | } 21 | 22 | // TODO Sound pitch 23 | @Override 24 | public ActionResultType useOn(ItemUseContext ctx) 25 | { 26 | World world = ctx.getLevel(); 27 | BlockPos pos = ctx.getClickedPos(); 28 | int id = getOrSetId(ctx.getItemInHand()); 29 | List match = LocksUtil.intersecting(world, pos).filter(lkb -> lkb.lock.id == id).collect(Collectors.toList()); 30 | if(match.isEmpty()) 31 | return ActionResultType.PASS; 32 | world.playSound(ctx.getPlayer(), pos, LocksSoundEvents.LOCK_OPEN.get(), SoundCategory.BLOCKS, 1f, 1f); 33 | if(world.isClientSide) 34 | return ActionResultType.SUCCESS; 35 | for(Lockable lkb : match) 36 | lkb.lock.setLocked(!lkb.lock.isLocked()); 37 | return ActionResultType.SUCCESS; 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/AbstractFurnaceTileEntityMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.tileentity.AbstractFurnaceTileEntity; 10 | import net.minecraft.tileentity.TileEntity; 11 | import net.minecraft.util.Direction; 12 | import net.minecraftforge.common.capabilities.Capability; 13 | import net.minecraftforge.common.util.LazyOptional; 14 | import net.minecraftforge.items.CapabilityItemHandler; 15 | 16 | @Mixin(AbstractFurnaceTileEntity.class) 17 | public class AbstractFurnaceTileEntityMixin 18 | { 19 | @Inject(at = @At("HEAD"), method = "getCapability(Lnet/minecraftforge/common/capabilities/Capability;Lnet/minecraft/util/Direction;)Lnet/minecraftforge/common/util/LazyOptional;", cancellable = true, remap = false) 20 | private void getCapability(Capability cap, Direction side, CallbackInfoReturnable cir) 21 | { 22 | TileEntity te = (TileEntity) (Object) this; 23 | if(!te.isRemoved() && cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY && te.hasLevel() && LocksUtil.locked(te.getLevel(), te.getBlockPos())) 24 | cir.setReturnValue(LazyOptional.empty()); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ChestBlockMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import melonslise.locks.common.util.LocksUtil; 9 | import net.minecraft.block.BlockState; 10 | import net.minecraft.block.ChestBlock; 11 | import net.minecraft.item.BlockItemUseContext; 12 | import net.minecraft.state.properties.ChestType; 13 | import net.minecraft.util.Direction; 14 | import net.minecraft.util.math.BlockPos; 15 | import net.minecraft.world.World; 16 | 17 | @Mixin(ChestBlock.class) 18 | public class ChestBlockMixin 19 | { 20 | @Inject(at = @At("HEAD"), method = "candidatePartnerFacing(Lnet/minecraft/item/BlockItemUseContext;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/Direction;", cancellable = true) 21 | private void candidatePartnerFacing(BlockItemUseContext ctx, Direction dir, CallbackInfoReturnable cir) 22 | { 23 | World world = ctx.getLevel(); 24 | BlockPos pos = ctx.getClickedPos().relative(dir); 25 | BlockState state = world.getBlockState(pos); 26 | cir.setReturnValue(state.is((ChestBlock) (Object) this) && state.getValue(ChestBlock.TYPE) == ChestType.SINGLE && !LocksUtil.locked(world, pos) ? state.getValue(ChestBlock.FACING) : null); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksContainerTypes.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.container.KeyRingContainer; 5 | import melonslise.locks.common.container.LockPickingContainer; 6 | import net.minecraft.inventory.container.Container; 7 | import net.minecraft.inventory.container.ContainerType; 8 | import net.minecraftforge.fml.RegistryObject; 9 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | public final class LocksContainerTypes 14 | { 15 | public static final DeferredRegister> CONTAINER_TYPES = DeferredRegister.create(ForgeRegistries.CONTAINERS, Locks.ID); 16 | 17 | public static final RegistryObject> 18 | LOCK_PICKING = add("lock_picking", new ContainerType(LockPickingContainer.FACTORY)); 19 | 20 | public static final RegistryObject> 21 | KEY_RING = add("key_ring", new ContainerType(KeyRingContainer.FACTORY)); 22 | 23 | private LocksContainerTypes() {} 24 | 25 | public static void register() 26 | { 27 | CONTAINER_TYPES.register(FMLJavaModLoadingContext.get().getModEventBus()); 28 | } 29 | 30 | public static RegistryObject> add(String name, ContainerType type) 31 | { 32 | return CONTAINER_TYPES.register(name, () -> type); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ExplosionContextMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.Optional; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 9 | 10 | import melonslise.locks.common.item.LockItem; 11 | import melonslise.locks.common.util.LocksPredicates; 12 | import melonslise.locks.common.util.LocksUtil; 13 | import net.minecraft.block.BlockState; 14 | import net.minecraft.fluid.FluidState; 15 | import net.minecraft.util.math.BlockPos; 16 | import net.minecraft.world.Explosion; 17 | import net.minecraft.world.ExplosionContext; 18 | import net.minecraft.world.IBlockReader; 19 | 20 | @Mixin(ExplosionContext.class) 21 | public class ExplosionContextMixin 22 | { 23 | @Inject(at = @At("RETURN"), method = "getBlockExplosionResistance(Lnet/minecraft/world/Explosion;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/fluid/FluidState;)Ljava/util/Optional;", cancellable = true) 24 | private void getBlockExplosionResistance(Explosion ex, IBlockReader world, BlockPos pos, BlockState state, FluidState fluid, CallbackInfoReturnable> cir) 25 | { 26 | cir.setReturnValue(cir.getReturnValue().map(r -> Math.max(r, LocksUtil.intersecting(ex.level, pos).filter(LocksPredicates.LOCKED).findFirst().map(lkb -> LockItem.getResistance(lkb.stack)).orElse(0)))); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/Locks.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import melonslise.locks.common.config.LocksClientConfig; 7 | import melonslise.locks.common.config.LocksConfig; 8 | import melonslise.locks.common.config.LocksServerConfig; 9 | import melonslise.locks.common.init.LocksContainerTypes; 10 | import melonslise.locks.common.init.LocksEnchantments; 11 | import melonslise.locks.common.init.LocksFeatures; 12 | import melonslise.locks.common.init.LocksItems; 13 | import melonslise.locks.common.init.LocksPlacements; 14 | import melonslise.locks.common.init.LocksRecipeSerializers; 15 | import melonslise.locks.common.init.LocksSoundEvents; 16 | import net.minecraftforge.fml.ModLoadingContext; 17 | import net.minecraftforge.fml.common.Mod; 18 | import net.minecraftforge.fml.config.ModConfig.Type; 19 | 20 | @Mod(Locks.ID) 21 | public final class Locks 22 | { 23 | public static final String ID = "locks"; 24 | 25 | public static final Logger LOGGER = LogManager.getLogger(); 26 | 27 | public Locks() 28 | { 29 | ModLoadingContext.get().registerConfig(Type.SERVER, LocksServerConfig.SPEC); 30 | ModLoadingContext.get().registerConfig(Type.COMMON, LocksConfig.SPEC); 31 | ModLoadingContext.get().registerConfig(Type.CLIENT, LocksClientConfig.SPEC); 32 | 33 | LocksItems.register(); 34 | LocksEnchantments.register(); 35 | LocksSoundEvents.register(); 36 | LocksPlacements.register(); 37 | LocksFeatures.register(); 38 | LocksContainerTypes.register(); 39 | LocksRecipeSerializers.register(); 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/init/LocksRenderTypes.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.init; 2 | 3 | import java.util.OptionalDouble; 4 | 5 | import org.lwjgl.opengl.GL11; 6 | 7 | import melonslise.locks.Locks; 8 | import net.minecraft.client.renderer.RenderState; 9 | import net.minecraft.client.renderer.RenderType; 10 | import net.minecraft.client.renderer.vertex.DefaultVertexFormats; 11 | import net.minecraft.client.renderer.vertex.VertexFormat; 12 | import net.minecraftforge.api.distmarker.Dist; 13 | import net.minecraftforge.api.distmarker.OnlyIn; 14 | 15 | @OnlyIn(Dist.CLIENT) 16 | public final class LocksRenderTypes extends RenderType 17 | { 18 | // FIXME this still has depth for some reason. As suggested we could try to create a custom DepthTestState which clears GL_DEPTH_BUFFER_BIT on setup, but thats not possible without AT or reflect... 19 | public static final RenderType OVERLAY_LINES = RenderType.create(Locks.ID + ".overlay_lines", DefaultVertexFormats.POSITION_COLOR, GL11.GL_LINES, 256, RenderType.State.builder() 20 | .setLineState(new LineState(OptionalDouble.empty())) 21 | .setLayeringState(RenderState.VIEW_OFFSET_Z_LAYERING) 22 | .setTransparencyState(RenderState.TRANSLUCENT_TRANSPARENCY) 23 | .setDepthTestState(RenderState.NO_DEPTH_TEST) 24 | .setWriteMaskState(RenderState.COLOR_WRITE) 25 | .createCompositeState(false)); 26 | 27 | private LocksRenderTypes(String name, VertexFormat format, int mode, int bufSize, boolean useDelegate, boolean sorting, Runnable setup, Runnable clear) 28 | { 29 | super(name, format, mode, bufSize, useDelegate, sorting, setup, clear); 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/network/toclient/TryPinResultPacket.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.network.toclient; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import melonslise.locks.common.container.LockPickingContainer; 6 | import melonslise.locks.common.init.LocksContainerTypes; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.inventory.container.Container; 9 | import net.minecraft.network.PacketBuffer; 10 | import net.minecraftforge.fml.network.NetworkEvent; 11 | 12 | public class TryPinResultPacket 13 | { 14 | private final boolean correct, reset; 15 | 16 | public TryPinResultPacket(boolean correct, boolean reset) 17 | { 18 | this.correct = correct; 19 | this.reset = reset; 20 | } 21 | 22 | public static TryPinResultPacket decode(PacketBuffer buf) 23 | { 24 | return new TryPinResultPacket(buf.readBoolean(), buf.readBoolean()); 25 | } 26 | 27 | public static void encode(TryPinResultPacket pkt, PacketBuffer buf) 28 | { 29 | buf.writeBoolean(pkt.correct); 30 | buf.writeBoolean(pkt.reset); 31 | } 32 | 33 | public static void handle(TryPinResultPacket pkt, Supplier ctx) 34 | { 35 | // Use runnable, lambda causes issues with class loading 36 | ctx.get().enqueueWork(new Runnable() 37 | { 38 | @Override 39 | public void run() 40 | { 41 | Container container = Minecraft.getInstance().player.containerMenu; 42 | if(container.getType() == LocksContainerTypes.LOCK_PICKING.get()) 43 | ((LockPickingContainer) container).handlePin(pkt.correct, pkt.reset); 44 | } 45 | }); 46 | ctx.get().setPacketHandled(true); 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/network/toclient/UpdateLockablePacket.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.network.toclient; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import melonslise.locks.common.init.LocksCapabilities; 6 | import melonslise.locks.common.util.Lockable; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.network.PacketBuffer; 9 | import net.minecraftforge.fml.network.NetworkEvent; 10 | 11 | public class UpdateLockablePacket 12 | { 13 | private final int id; 14 | // Expandable 15 | private final boolean locked; 16 | 17 | public UpdateLockablePacket(int id, boolean locked) 18 | { 19 | this.id = id; 20 | this.locked = locked; 21 | } 22 | 23 | public UpdateLockablePacket(Lockable lkb) 24 | { 25 | this(lkb.id, lkb.lock.isLocked()); 26 | } 27 | 28 | public static UpdateLockablePacket decode(PacketBuffer buf) 29 | { 30 | return new UpdateLockablePacket(buf.readInt(), buf.readBoolean()); 31 | } 32 | 33 | public static void encode(UpdateLockablePacket pkt, PacketBuffer buf) 34 | { 35 | buf.writeInt(pkt.id); 36 | buf.writeBoolean(pkt.locked); 37 | } 38 | 39 | public static void handle(UpdateLockablePacket pkt, Supplier ctx) 40 | { 41 | // Use runnable, lambda causes issues with class loading 42 | ctx.get().enqueueWork(new Runnable() 43 | { 44 | @Override 45 | public void run() 46 | { 47 | Minecraft.getInstance().level.getCapability(LocksCapabilities.LOCKABLE_HANDLER).ifPresent(handler -> handler.getLoaded().get(pkt.id).lock.setLocked(pkt.locked)); 48 | } 49 | }); 50 | ctx.get().setPacketHandled(true); 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksEnchantments.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.enchantment.ComplexityEnchantment; 5 | import melonslise.locks.common.enchantment.ShockingEnchantment; 6 | import melonslise.locks.common.enchantment.SturdyEnchantment; 7 | import melonslise.locks.common.item.LockItem; 8 | import net.minecraft.enchantment.Enchantment; 9 | import net.minecraft.enchantment.EnchantmentType; 10 | import net.minecraftforge.fml.RegistryObject; 11 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 12 | import net.minecraftforge.registries.DeferredRegister; 13 | import net.minecraftforge.registries.ForgeRegistries; 14 | 15 | public final class LocksEnchantments 16 | { 17 | public static final EnchantmentType LOCK_TYPE = EnchantmentType.create("LOCK", item -> item instanceof LockItem); // FIXME check if is in tag instead? 18 | 19 | public static final DeferredRegister ENCHANTMENTS = DeferredRegister.create(ForgeRegistries.ENCHANTMENTS, Locks.ID); 20 | 21 | public static final RegistryObject 22 | SHOCKING = add("shocking", new ShockingEnchantment()), 23 | STURDY = add("sturdy", new SturdyEnchantment()), 24 | COMPLEXITY = add("complexity", new ComplexityEnchantment()); 25 | 26 | private LocksEnchantments() {} 27 | 28 | public static void register() 29 | { 30 | ENCHANTMENTS.register(FMLJavaModLoadingContext.get().getModEventBus()); 31 | } 32 | 33 | public static RegistryObject add(String name, Enchantment ench) 34 | { 35 | return ENCHANTMENTS.register(name, () -> ench); 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/shipwreck_supply.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 3.0, 10 | "max": 10.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:gold_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:iron_lock_pick", 35 | "weight": 3, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 1.0, 43 | "max": 2.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:wood_lock_pick", 52 | "weight": 5, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:iron_lock_mechanism", 69 | "weight": 1 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:wood_lock_mechanism", 74 | "weight": 2 75 | } 76 | ] 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/client/WorldRendererMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin.client; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.matrix.MatrixStack; 9 | 10 | import melonslise.locks.client.event.LocksClientForgeEvents; 11 | import melonslise.locks.client.util.LocksClientUtil; 12 | import net.minecraft.client.Minecraft; 13 | import net.minecraft.client.renderer.ActiveRenderInfo; 14 | import net.minecraft.client.renderer.GameRenderer; 15 | import net.minecraft.client.renderer.LightTexture; 16 | import net.minecraft.client.renderer.WorldRenderer; 17 | import net.minecraft.util.math.vector.Matrix4f; 18 | 19 | @Mixin(WorldRenderer.class) 20 | public class WorldRendererMixin 21 | { 22 | // Before first checkPoseStack call 23 | @Inject(at = @At(value = "INVOKE", target = "net/minecraft/client/renderer/WorldRenderer.checkPoseStack(Lcom/mojang/blaze3d/matrix/MatrixStack;)V", ordinal = 0), method = "renderLevel(Lcom/mojang/blaze3d/matrix/MatrixStack;FJZLnet/minecraft/client/renderer/ActiveRenderInfo;Lnet/minecraft/client/renderer/GameRenderer;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/util/math/vector/Matrix4f;)V") 24 | private void renderLevel(MatrixStack mtx, float pt, long nanoTime, boolean renderOutline, ActiveRenderInfo cam, GameRenderer gr, LightTexture lightTex, Matrix4f proj, CallbackInfo ci) 25 | { 26 | LocksClientForgeEvents.renderLocks(mtx, Minecraft.getInstance().renderBuffers().bufferSource(), LocksClientUtil.getClippingHelper(mtx, proj), pt); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/action/AccelerateAction.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite.action; 2 | 3 | import melonslise.locks.client.gui.sprite.Sprite; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | 7 | @OnlyIn(Dist.CLIENT) 8 | public class AccelerateAction extends MoveAction 9 | { 10 | public float accelX, accelY; 11 | 12 | public AccelerateAction(float speedX, float speedY, float accelX, float accelY) 13 | { 14 | super(speedX, speedY); 15 | this.accelX = accelX; 16 | this.accelY = accelY; 17 | } 18 | 19 | public static AccelerateAction at(float speedX, float speedY, float accelX, float accelY) 20 | { 21 | return new AccelerateAction(speedX, speedY, accelX, accelY); 22 | } 23 | 24 | public static AccelerateAction to(float shiftX, float shiftY, int ticks, boolean accel) 25 | { 26 | float speedX = 2f * shiftX / ticks / (accel ? 3f : 1f); 27 | float speedY = 2f * shiftY / ticks / (accel ? 3f : 1f); 28 | float accelX = speedX / ticks * (accel ? 1f : -1f); 29 | float accelY = speedY / ticks * (accel ? 1f : -1f); 30 | return (AccelerateAction) at(speedX, speedY, accelX, accelY).time(ticks); 31 | } 32 | 33 | public static AccelerateAction to(Sprite sprite, float posX, float posY, int ticks, boolean accel) 34 | { 35 | return to(posX - sprite.posX, posY - sprite.posY, ticks, accel); 36 | } 37 | 38 | @Override 39 | public void update(S sprite) 40 | { 41 | super.update(sprite); 42 | sprite.posX += this.accelX * 0.5f; 43 | sprite.posY += this.accelY * 0.5f; 44 | this.speedX += this.accelX; 45 | this.speedY += this.accelY; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/KeyRingScreen.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui; 2 | 3 | import com.mojang.blaze3d.matrix.MatrixStack; 4 | 5 | import melonslise.locks.common.container.KeyRingContainer; 6 | import net.minecraft.client.gui.screen.inventory.ContainerScreen; 7 | import net.minecraft.entity.player.PlayerInventory; 8 | import net.minecraft.util.ResourceLocation; 9 | import net.minecraft.util.text.ITextComponent; 10 | import net.minecraftforge.api.distmarker.Dist; 11 | import net.minecraftforge.api.distmarker.OnlyIn; 12 | 13 | @OnlyIn(Dist.CLIENT) 14 | public class KeyRingScreen extends ContainerScreen 15 | { 16 | public static final ResourceLocation TEXTURE = new ResourceLocation("textures/gui/container/generic_54.png"); 17 | 18 | public KeyRingScreen(KeyRingContainer cont, PlayerInventory inv, ITextComponent title) 19 | { 20 | super(cont, inv, title); 21 | this.imageHeight = 114 + cont.rows * 18; 22 | this.inventoryLabelY = this.imageHeight - 94; 23 | } 24 | 25 | @Override 26 | public void render(MatrixStack mtx, int mouseX, int mouseY, float partialTick) 27 | { 28 | this.renderBackground(mtx); 29 | super.render(mtx, mouseX, mouseY, partialTick); 30 | this.renderTooltip(mtx, mouseX, mouseY); 31 | } 32 | 33 | @Override 34 | protected void renderBg(MatrixStack mtx, float partialTick, int mouseX, int mouseY) 35 | { 36 | int rows = this.getMenu().rows; 37 | this.minecraft.getTextureManager().bind(TEXTURE); 38 | int cornerX = (this.width - this.imageWidth) / 2; 39 | int cornerY = (this.height - this.imageHeight) / 2; 40 | this.blit(mtx, cornerX, cornerY, 0, 0, this.imageWidth, rows * 18 + 17); 41 | this.blit(mtx, cornerX, cornerY + rows * 18 + 17, 0, 126, this.imageWidth, 96); 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/init/LocksItemModelsProperties.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.init.LocksItems; 5 | import melonslise.locks.common.item.LockItem; 6 | import net.minecraft.item.IItemPropertyGetter; 7 | import net.minecraft.item.ItemModelsProperties; 8 | import net.minecraft.util.ResourceLocation; 9 | import net.minecraftforge.api.distmarker.Dist; 10 | import net.minecraftforge.api.distmarker.OnlyIn; 11 | import net.minecraftforge.items.CapabilityItemHandler; 12 | 13 | @OnlyIn(Dist.CLIENT) 14 | public final class LocksItemModelsProperties 15 | { 16 | private LocksItemModelsProperties() {} 17 | 18 | public static void register() 19 | { 20 | ItemModelsProperties.register(LocksItems.KEY_RING.get(), new ResourceLocation(Locks.ID, "keys"), (stack, world, entity) -> 21 | { 22 | return stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) 23 | .map(inv -> 24 | { 25 | int keys = 0; 26 | for(int a = 0; a < inv.getSlots(); ++a) 27 | if(!inv.getStackInSlot(a).isEmpty()) 28 | ++keys; 29 | return (float) keys / inv.getSlots(); 30 | }) 31 | .orElse(0f); 32 | }); 33 | ResourceLocation id = new ResourceLocation(Locks.ID, "open"); 34 | IItemPropertyGetter getter = (stack, world, entity) -> LockItem.isOpen(stack) ? 1f : 0f; 35 | ItemModelsProperties.register(LocksItems.WOOD_LOCK.get(), id, getter); 36 | ItemModelsProperties.register(LocksItems.IRON_LOCK.get(), id, getter); 37 | ItemModelsProperties.register(LocksItems.STEEL_LOCK.get(), id, getter); 38 | ItemModelsProperties.register(LocksItems.GOLD_LOCK.get(), id, getter); 39 | ItemModelsProperties.register(LocksItems.DIAMOND_LOCK.get(), id, getter); 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/jungle_temple.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 2.0, 10 | "max": 6.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 10, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 10, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 5 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:iron_lock_mechanism", 74 | "weight": 5 75 | }, 76 | { 77 | "type": "minecraft:item", 78 | "name": "locks:wood_lock_mechanism", 79 | "weight": 10 80 | } 81 | ] 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/pillager_outpost.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "locks", 7 | "rolls": 8 | { 9 | "min": 0.0, 10 | "max": 2.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 10, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 10, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 5 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:iron_lock_mechanism", 74 | "weight": 5 75 | }, 76 | { 77 | "type": "minecraft:item", 78 | "name": "locks:wood_lock_mechanism", 79 | "weight": 10 80 | } 81 | ] 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/stronghold_corridor.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 2.0, 10 | "max": 6.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 10, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 10, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 5 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:iron_lock_mechanism", 74 | "weight": 5 75 | }, 76 | { 77 | "type": "minecraft:item", 78 | "name": "locks:wood_lock_mechanism", 79 | "weight": 10 80 | } 81 | ] 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/stronghold_crossing.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 2.0, 10 | "max": 6.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 10, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 10, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 5 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:iron_lock_mechanism", 74 | "weight": 5 75 | }, 76 | { 77 | "type": "minecraft:item", 78 | "name": "locks:wood_lock_mechanism", 79 | "weight": 10 80 | } 81 | ] 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/village/village_toolsmith.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 2.0, 10 | "max": 6.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 10, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 10, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 5 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:iron_lock_mechanism", 74 | "weight": 5 75 | }, 76 | { 77 | "type": "minecraft:item", 78 | "name": "locks:wood_lock_mechanism", 79 | "weight": 10 80 | } 81 | ] 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ChunkMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import melonslise.locks.common.capability.ILockableHandler; 9 | import melonslise.locks.common.capability.ILockableStorage; 10 | import melonslise.locks.common.init.LocksCapabilities; 11 | import melonslise.locks.common.init.LocksNetwork; 12 | import melonslise.locks.common.network.toclient.AddLockableToChunkPacket; 13 | import melonslise.locks.common.util.ILockableProvider; 14 | import melonslise.locks.common.util.Lockable; 15 | import net.minecraft.world.World; 16 | import net.minecraft.world.chunk.Chunk; 17 | import net.minecraft.world.chunk.ChunkPrimer; 18 | import net.minecraftforge.fml.network.PacketDistributor; 19 | 20 | @Mixin(Chunk.class) 21 | public class ChunkMixin 22 | { 23 | @Inject(at = @At("TAIL"), method = "(Lnet/minecraft/world/World;Lnet/minecraft/world/chunk/ChunkPrimer;)V") 24 | private void init(World world, ChunkPrimer pr, CallbackInfo ci) 25 | { 26 | Chunk ch = (Chunk) (Object) this; 27 | ILockableStorage st = ch.getCapability(LocksCapabilities.LOCKABLE_STORAGE).orElse(null); 28 | ILockableHandler handler = world.getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null); 29 | // We trust that all checks pass (such as volume and intersect checks) due to this happening only during world gen 30 | for(Lockable lkb : ((ILockableProvider) pr).getLockables()) 31 | { 32 | st.add(lkb); 33 | handler.getLoaded().put(lkb.id, lkb); 34 | lkb.addObserver(handler); 35 | LocksNetwork.MAIN.send(PacketDistributor.TRACKING_CHUNK.with(() -> ch), new AddLockableToChunkPacket(lkb, ch)); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/ServerWorldMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.stream.Collectors; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | import melonslise.locks.common.capability.ILockableHandler; 11 | import melonslise.locks.common.init.LocksCapabilities; 12 | import net.minecraft.block.BlockState; 13 | import net.minecraft.entity.item.ItemEntity; 14 | import net.minecraft.util.SoundCategory; 15 | import net.minecraft.util.SoundEvents; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.world.server.ServerWorld; 18 | 19 | @Mixin(ServerWorld.class) 20 | public class ServerWorldMixin 21 | { 22 | @Inject(at = @At("HEAD"), method = "sendBlockUpdated(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V") 23 | private void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flag, CallbackInfo ci) 24 | { 25 | if(oldState.is(newState.getBlock())) 26 | return; 27 | ServerWorld world = (ServerWorld) (Object) this; 28 | ILockableHandler handler = world.getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null); 29 | // create buffer list because otherwise we will be deleting elements while iterating (BAD!!) 30 | handler.getInChunk(pos).values().stream().filter(lkb -> lkb.bb.intersects(pos)).collect(Collectors.toList()).forEach(lkb -> 31 | { 32 | world.playSound(null, pos, SoundEvents.ITEM_BREAK, SoundCategory.BLOCKS, 0.8f, 0.8f + world.random.nextFloat() * 0.4f); 33 | world.addFreshEntity(new ItemEntity(world, pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d, lkb.stack)); 34 | handler.remove(lkb.id); 35 | }); 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksNetwork.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.network.toclient.AddLockablePacket; 5 | import melonslise.locks.common.network.toclient.AddLockableToChunkPacket; 6 | import melonslise.locks.common.network.toclient.RemoveLockablePacket; 7 | import melonslise.locks.common.network.toclient.TryPinResultPacket; 8 | import melonslise.locks.common.network.toclient.UpdateLockablePacket; 9 | import melonslise.locks.common.network.toserver.TryPinPacket; 10 | import net.minecraft.util.ResourceLocation; 11 | import net.minecraftforge.fml.network.NetworkRegistry; 12 | import net.minecraftforge.fml.network.simple.SimpleChannel; 13 | 14 | public final class LocksNetwork 15 | { 16 | public static final SimpleChannel MAIN = NetworkRegistry.newSimpleChannel(new ResourceLocation(Locks.ID, "main"), () -> "locks", a -> true, a -> true); 17 | 18 | private LocksNetwork() {} 19 | 20 | public static void register() 21 | { 22 | MAIN.registerMessage(0, AddLockablePacket.class, AddLockablePacket::encode, AddLockablePacket::decode, AddLockablePacket::handle); 23 | MAIN.registerMessage(1, AddLockableToChunkPacket.class, AddLockableToChunkPacket::encode, AddLockableToChunkPacket::decode, AddLockableToChunkPacket::handle); 24 | MAIN.registerMessage(2, RemoveLockablePacket.class, RemoveLockablePacket::encode, RemoveLockablePacket::decode, RemoveLockablePacket::handle); 25 | MAIN.registerMessage(3, UpdateLockablePacket.class, UpdateLockablePacket::encode, UpdateLockablePacket::decode, UpdateLockablePacket::handle); 26 | MAIN.registerMessage(4, TryPinPacket.class, TryPinPacket::encode, TryPinPacket::decode, TryPinPacket::handle); 27 | MAIN.registerMessage(5, TryPinResultPacket.class, TryPinResultPacket::encode, TryPinResultPacket::decode, TryPinResultPacket::handle); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/Transform.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import java.util.HashMap; 4 | 5 | import org.apache.commons.lang3.tuple.Pair; 6 | 7 | import net.minecraft.state.properties.AttachFace; 8 | import net.minecraft.util.Direction; 9 | 10 | public enum Transform 11 | { 12 | NORTH_UP(Direction.NORTH, AttachFace.CEILING), 13 | SOUTH_UP(Direction.SOUTH, AttachFace.CEILING), 14 | WEST_UP(Direction.WEST, AttachFace.CEILING), 15 | EAST_UP(Direction.EAST, AttachFace.CEILING), 16 | NORTH_MID(Direction.NORTH, AttachFace.WALL), 17 | SOUTH_MID(Direction.SOUTH, AttachFace.WALL), 18 | WEST_MID(Direction.WEST, AttachFace.WALL), 19 | EAST_MID(Direction.EAST, AttachFace.WALL), 20 | NORTH_DOWN(Direction.NORTH, AttachFace.FLOOR), 21 | SOUTH_DOWN(Direction.SOUTH, AttachFace.FLOOR), 22 | WEST_DOWN(Direction.WEST, AttachFace.FLOOR), 23 | EAST_DOWN(Direction.EAST, AttachFace.FLOOR); 24 | 25 | public static final HashMap, Transform> LOOKUP = new HashMap<>(16); // 12 / 0.75 26 | 27 | static 28 | { 29 | for(Transform tr : Transform.values()) 30 | LOOKUP.put(Pair.of(tr.dir, tr.face), tr); 31 | } 32 | 33 | public final Direction dir; 34 | public final AttachFace face; 35 | 36 | Transform(Direction dir, AttachFace face) 37 | { 38 | this.dir = dir; 39 | this.face = face; 40 | } 41 | 42 | public Direction getCuboidFace() 43 | { 44 | return this.face == AttachFace.CEILING ? Direction.UP : this.face == AttachFace.FLOOR ? Direction.DOWN : this.dir; 45 | } 46 | 47 | public static Transform fromDirectionAndFace(Direction dir, AttachFace face, Direction def) 48 | { 49 | return LOOKUP.get(Pair.of(dir.getAxis() == Direction.Axis.Y ? def : dir, face)); 50 | } 51 | 52 | public static Transform fromDirection(Direction dir, Direction def) 53 | { 54 | return fromDirectionAndFace(dir, LocksUtil.faceFromDir(dir), def); 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/worldgen/LockChestsFeature.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.worldgen; 2 | 3 | import java.util.Random; 4 | 5 | import com.mojang.serialization.Codec; 6 | 7 | import melonslise.locks.common.config.LocksConfig; 8 | import melonslise.locks.common.util.Cuboid6i; 9 | import melonslise.locks.common.util.ILockableProvider; 10 | import melonslise.locks.common.util.Lock; 11 | import melonslise.locks.common.util.Lockable; 12 | import melonslise.locks.common.util.Transform; 13 | import net.minecraft.block.BlockState; 14 | import net.minecraft.block.ChestBlock; 15 | import net.minecraft.item.ItemStack; 16 | import net.minecraft.state.properties.ChestType; 17 | import net.minecraft.util.Direction; 18 | import net.minecraft.util.math.BlockPos; 19 | import net.minecraft.world.ISeedReader; 20 | import net.minecraft.world.gen.ChunkGenerator; 21 | import net.minecraft.world.gen.feature.Feature; 22 | import net.minecraft.world.gen.feature.NoFeatureConfig; 23 | 24 | public class LockChestsFeature extends Feature 25 | { 26 | public LockChestsFeature(Codec codec) 27 | { 28 | super(codec); 29 | } 30 | 31 | @Override 32 | public boolean place(ISeedReader world, ChunkGenerator gen, Random rng, BlockPos pos, NoFeatureConfig cfg) 33 | { 34 | if(!LocksConfig.canGen(rng)) 35 | return false; 36 | BlockState state = world.getBlockState(pos); 37 | BlockPos pos1 = state.getValue(ChestBlock.TYPE) == ChestType.SINGLE ? pos : pos.relative(ChestBlock.getConnectedDirection(state)); 38 | ItemStack stack = LocksConfig.getRandomLock(rng); 39 | Lockable lkb = new Lockable(new Cuboid6i(pos, pos1), Lock.from(stack), Transform.fromDirection(state.getValue(ChestBlock.FACING), Direction.NORTH), stack, world.getLevel()); 40 | lkb.bb.getContainedChunks((x, z) -> 41 | { 42 | ((ILockableProvider) world.getChunk(x, z)).getLockables().add(lkb); 43 | return true; 44 | }); 45 | return true; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/item/LockingItem.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.item; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.ThreadLocalRandom; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | import melonslise.locks.Locks; 9 | import net.minecraft.client.util.ITooltipFlag; 10 | import net.minecraft.entity.Entity; 11 | import net.minecraft.item.Item; 12 | import net.minecraft.item.ItemStack; 13 | import net.minecraft.nbt.CompoundNBT; 14 | import net.minecraft.util.text.ITextComponent; 15 | import net.minecraft.util.text.TextFormatting; 16 | import net.minecraft.util.text.TranslationTextComponent; 17 | import net.minecraft.world.World; 18 | import net.minecraftforge.api.distmarker.Dist; 19 | import net.minecraftforge.api.distmarker.OnlyIn; 20 | 21 | public class LockingItem extends Item 22 | { 23 | public LockingItem(Properties props) 24 | { 25 | super(props.stacksTo(1)); 26 | } 27 | 28 | public static final String KEY_ID = "Id"; 29 | 30 | public static ItemStack copyId(ItemStack from, ItemStack to) 31 | { 32 | to.getOrCreateTag().putInt(KEY_ID, getOrSetId(from)); 33 | return to; 34 | } 35 | 36 | public static int getOrSetId(ItemStack stack) 37 | { 38 | CompoundNBT nbt = stack.getOrCreateTag(); 39 | if(!nbt.contains(KEY_ID)) 40 | nbt.putInt(KEY_ID, ThreadLocalRandom.current().nextInt()); 41 | return nbt.getInt(KEY_ID); 42 | } 43 | 44 | @Override 45 | public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) 46 | { 47 | if(!world.isClientSide) 48 | getOrSetId(stack); 49 | } 50 | 51 | @OnlyIn(Dist.CLIENT) 52 | @Override 53 | public void appendHoverText(ItemStack stack, @Nullable World world, List lines, ITooltipFlag flag) 54 | { 55 | if(stack.hasTag() && stack.getTag().contains(KEY_ID)) 56 | lines.add(new TranslationTextComponent(Locks.ID + ".tooltip.id", ItemStack.ATTRIBUTE_MODIFIER_FORMAT.format(getOrSetId(stack))).withStyle(TextFormatting.DARK_GREEN)); 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/resources/assets/locks/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "itemGroup.locks": "Locks", 3 | 4 | "item.locks.spring": "Spring", 5 | "item.locks.wood_lock_mechanism": "Wood Lock Mechanism", 6 | "item.locks.iron_lock_mechanism": "Iron Lock Mechanism", 7 | "item.locks.steel_lock_mechanism": "Steel Lock Mechanism", 8 | "item.locks.wood_lock": "Wood Lock", 9 | "item.locks.iron_lock": "Iron Lock", 10 | "item.locks.steel_lock": "Steel Lock", 11 | "item.locks.gold_lock": "Gold Plated Lock", 12 | "item.locks.diamond_lock": "Diamond Plated Lock", 13 | "item.locks.wood_lock_pick": "Bobby Pin Lock Pick", 14 | "item.locks.iron_lock_pick": "Iron Lock Pick", 15 | "item.locks.steel_lock_pick": "Steel Lock Pick", 16 | "item.locks.gold_lock_pick": "Gold Lock Pick", 17 | "item.locks.diamond_lock_pick": "Diamond Lock Pick", 18 | "item.locks.key": "Key", 19 | "item.locks.master_key": "Master Key", 20 | "item.locks.key_blank": "Key Blank", 21 | "item.locks.key_ring": "Key Ring", 22 | 23 | "enchantment.locks.shocking": "Shocking", 24 | "enchantment.locks.shocking.desc": "Shocks thieves that fail to pick the lock", 25 | "enchantment.locks.sturdy": "Sturdy", 26 | "enchantment.locks.sturdy.desc": "Reduces the strength of lock picks", 27 | "enchantment.locks.complexity": "Complexity", 28 | "enchantment.locks.complexity.desc": "Makes the lock too complex to pick with lower tier lock picks", 29 | 30 | "death.attack.locks.shock": "%1$s was electrocuted after failing to pick an enchanted lock", 31 | "death.attack.locks.shock.player": "%1$s was electrocuted by an enchanted lock whilst simultaneously picking it and fighting off %2$s", 32 | 33 | "locks.gui.lockpicking.title": "Lock picking", 34 | "locks.gui.lockpicking.open": "Press `ESC` to open", 35 | 36 | "locks.subtitle.key_ring": "Keys jingle", 37 | "locks.subtitle.lock.close": "Lock clicks", 38 | "locks.subtitle.lock.open": "Key turned in lock", 39 | "locks.subtitle.lock.rattle": "Lock rattles", 40 | "locks.subtitle.pin.fail": "Pin clacks hollowly", 41 | "locks.subtitle.pin.match": "Pin clicks lightly", 42 | "locks.subtitle.shock": "Sudden electric zap", 43 | 44 | "locks.tooltip.id": "ID: %s", 45 | "locks.tooltip.length": "Length: %s", 46 | "locks.tooltip.strength": "Strength: %s", 47 | 48 | "locks.status.locked": "This block is locked", 49 | "locks.status.too_complex": "This lock is too complex for this lock pick" 50 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/LockableStorage.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; 4 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 5 | import melonslise.locks.Locks; 6 | import melonslise.locks.common.init.LocksCapabilities; 7 | import melonslise.locks.common.util.Lockable; 8 | import net.minecraft.nbt.CompoundNBT; 9 | import net.minecraft.nbt.ListNBT; 10 | import net.minecraft.util.ResourceLocation; 11 | import net.minecraft.world.chunk.Chunk; 12 | 13 | /* 14 | * Internal storage for lockables with almost no handling logic 15 | * Also stores lockables which are shared by multiple chunks. Duplicate shared lockables are handled by checking if they have already been loaded before 16 | */ 17 | public class LockableStorage implements ILockableStorage 18 | { 19 | public static final ResourceLocation ID = new ResourceLocation(Locks.ID, "lockable_storage"); 20 | 21 | public final Chunk chunk; 22 | 23 | public Int2ObjectMap lockables = new Int2ObjectLinkedOpenHashMap(); 24 | 25 | public LockableStorage(Chunk chunk) 26 | { 27 | this.chunk = chunk; 28 | } 29 | 30 | @Override 31 | public Int2ObjectMap get() 32 | { 33 | return this.lockables; 34 | } 35 | 36 | @Override 37 | public void add(Lockable lkb) 38 | { 39 | this.lockables.put(lkb.id, lkb); 40 | this.chunk.markUnsaved(); 41 | } 42 | 43 | @Override 44 | public void remove(int id) 45 | { 46 | this.lockables.remove(id); 47 | this.chunk.markUnsaved(); 48 | } 49 | 50 | @Override 51 | public ListNBT serializeNBT() 52 | { 53 | ListNBT list = new ListNBT(); 54 | for(Lockable lkb : this.lockables.values()) 55 | list.add(Lockable.toNbt(lkb)); 56 | return list; 57 | } 58 | 59 | @Override 60 | public void deserializeNBT(ListNBT nbt) 61 | { 62 | ILockableHandler handler = this.chunk.getLevel().getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null); 63 | Int2ObjectMap lkbs = handler.getLoaded(); 64 | for(int a = 0; a < nbt.size(); ++a) 65 | { 66 | CompoundNBT nbt1 = nbt.getCompound(a); 67 | Lockable lkb = lkbs.get(Lockable.idFromNbt(nbt1)); 68 | if(lkb == lkbs.defaultReturnValue()) 69 | { 70 | lkb = Lockable.fromNbt(nbt1); 71 | lkb.addObserver(handler); 72 | lkbs.put(lkb.id, lkb); 73 | } 74 | this.lockables.put(lkb.id, lkb); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/shipwreck_treasure.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 3.0, 10 | "max": 6.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 10, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 40, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 40, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 10 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:diamond_lock", 74 | "weight": 2, 75 | "functions": 76 | [ 77 | { 78 | "function": "minecraft:enchant_randomly" 79 | } 80 | ] 81 | }, 82 | { 83 | "type": "minecraft:item", 84 | "name": "locks:steel_lock", 85 | "weight": 4, 86 | "functions": 87 | [ 88 | { 89 | "function": "minecraft:enchant_randomly" 90 | } 91 | ] 92 | }, 93 | { 94 | "type": "minecraft:item", 95 | "name": "locks:gold_lock", 96 | "weight": 10, 97 | "functions": 98 | [ 99 | { 100 | "function": "minecraft:enchant_randomly" 101 | } 102 | ] 103 | } 104 | ] 105 | } 106 | ] 107 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/network/toclient/AddLockableToChunkPacket.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.network.toclient; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 6 | import melonslise.locks.common.capability.ILockableHandler; 7 | import melonslise.locks.common.capability.ILockableStorage; 8 | import melonslise.locks.common.init.LocksCapabilities; 9 | import melonslise.locks.common.util.Lockable; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.network.PacketBuffer; 12 | import net.minecraft.util.math.ChunkPos; 13 | import net.minecraft.world.chunk.Chunk; 14 | import net.minecraftforge.fml.network.NetworkEvent; 15 | 16 | public class AddLockableToChunkPacket 17 | { 18 | private final Lockable lockable; 19 | private final int x, z; 20 | 21 | public AddLockableToChunkPacket(Lockable lkb, int x, int z) 22 | { 23 | this.lockable = lkb; 24 | this.x = x; 25 | this.z = z; 26 | } 27 | 28 | public AddLockableToChunkPacket(Lockable lkb, ChunkPos pos) 29 | { 30 | this(lkb, pos.x, pos.z); 31 | } 32 | 33 | public AddLockableToChunkPacket(Lockable lkb, Chunk ch) 34 | { 35 | this(lkb, ch.getPos()); 36 | } 37 | 38 | public static AddLockableToChunkPacket decode(PacketBuffer buf) 39 | { 40 | return new AddLockableToChunkPacket(Lockable.fromBuf(buf), buf.readInt(), buf.readInt()); 41 | } 42 | 43 | public static void encode(AddLockableToChunkPacket pkt, PacketBuffer buf) 44 | { 45 | Lockable.toBuf(buf, pkt.lockable); 46 | buf.writeInt(pkt.x); 47 | buf.writeInt(pkt.z); 48 | } 49 | 50 | public static void handle(AddLockableToChunkPacket pkt, Supplier ctx) 51 | { 52 | // Use runnable, lambda causes issues with class loading 53 | ctx.get().enqueueWork(new Runnable() 54 | { 55 | @Override 56 | public void run() 57 | { 58 | Minecraft mc = Minecraft.getInstance(); 59 | ILockableStorage st = mc.level.getChunk(pkt.x, pkt.z).getCapability(LocksCapabilities.LOCKABLE_STORAGE).orElse(null); 60 | ILockableHandler handler = mc.level.getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null); 61 | Int2ObjectMap lkbs = handler.getLoaded(); 62 | Lockable lkb = lkbs.get(pkt.lockable.id); 63 | if(lkb == lkbs.defaultReturnValue()) 64 | { 65 | lkb = pkt.lockable; 66 | lkb.addObserver(handler); 67 | lkbs.put(lkb.id, lkb); 68 | } 69 | st.add(lkb); 70 | } 71 | }); 72 | ctx.get().setPacketHandled(true); 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/desert_pyramid.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 1.0, 10 | "max": 3.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 5, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 15, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:steel_lock_mechanism", 52 | "weight": 10 53 | } 54 | ] 55 | }, 56 | { 57 | "name": "pool1", 58 | "rolls": 59 | { 60 | "min": 1.0, 61 | "max": 4.0, 62 | "type": "minecraft:uniform" 63 | }, 64 | "entries": 65 | [ 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:iron_lock_pick", 69 | "weight": 5, 70 | "functions": 71 | [ 72 | { 73 | "function": "minecraft:set_count", 74 | "count": 75 | { 76 | "min": 2.0, 77 | "max": 4.0, 78 | "type": "minecraft:uniform" 79 | } 80 | } 81 | ] 82 | }, 83 | { 84 | "type": "minecraft:item", 85 | "name": "locks:gold_lock_pick", 86 | "weight": 5, 87 | "functions": 88 | [ 89 | { 90 | "function": "minecraft:set_count", 91 | "count": 92 | { 93 | "min": 2.0, 94 | "max": 4.0, 95 | "type": "minecraft:uniform" 96 | } 97 | } 98 | ] 99 | }, 100 | { 101 | "type": "minecraft:item", 102 | "name": "locks:iron_lock_mechanism", 103 | "weight": 5 104 | }, 105 | { 106 | "type": "minecraft:item", 107 | "name": "locks:wood_lock_mechanism", 108 | "weight": 10 109 | } 110 | ] 111 | } 112 | ] 113 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/simple_dungeon.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 1.0, 10 | "max": 3.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 5, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 15, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:steel_lock_mechanism", 52 | "weight": 10 53 | } 54 | ] 55 | }, 56 | { 57 | "name": "pool1", 58 | "rolls": 59 | { 60 | "min": 1.0, 61 | "max": 4.0, 62 | "type": "minecraft:uniform" 63 | }, 64 | "entries": 65 | [ 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:iron_lock_pick", 69 | "weight": 5, 70 | "functions": 71 | [ 72 | { 73 | "function": "minecraft:set_count", 74 | "count": 75 | { 76 | "min": 2.0, 77 | "max": 4.0, 78 | "type": "minecraft:uniform" 79 | } 80 | } 81 | ] 82 | }, 83 | { 84 | "type": "minecraft:item", 85 | "name": "locks:gold_lock_pick", 86 | "weight": 5, 87 | "functions": 88 | [ 89 | { 90 | "function": "minecraft:set_count", 91 | "count": 92 | { 93 | "min": 2.0, 94 | "max": 4.0, 95 | "type": "minecraft:uniform" 96 | } 97 | } 98 | ] 99 | }, 100 | { 101 | "type": "minecraft:item", 102 | "name": "locks:iron_lock_mechanism", 103 | "weight": 5 104 | }, 105 | { 106 | "type": "minecraft:item", 107 | "name": "locks:wood_lock_mechanism", 108 | "weight": 10 109 | } 110 | ] 111 | } 112 | ] 113 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/underwater_ruin_big.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 2.0, 10 | "max": 8.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:steel_lock_pick", 18 | "weight": 1, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:gold_lock_pick", 35 | "weight": 3, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 1.0, 43 | "max": 3.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:iron_lock_pick", 52 | "weight": 3, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 1.0, 60 | "max": 3.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | }, 68 | { 69 | "name": "pool1", 70 | "rolls": 1, 71 | "entries": 72 | [ 73 | { 74 | "type": "minecraft:item", 75 | "name": "locks:steel_lock_mechanism", 76 | "weight": 4 77 | }, 78 | { 79 | "type": "minecraft:item", 80 | "name": "locks:diamond_lock", 81 | "weight": 1, 82 | "functions": 83 | [ 84 | { 85 | "function": "minecraft:enchant_randomly" 86 | } 87 | ] 88 | }, 89 | { 90 | "type": "minecraft:item", 91 | "name": "locks:steel_lock", 92 | "weight": 2, 93 | "functions": 94 | [ 95 | { 96 | "function": "minecraft:enchant_randomly" 97 | } 98 | ] 99 | }, 100 | { 101 | "type": "minecraft:item", 102 | "name": "locks:gold_lock", 103 | "weight": 4, 104 | "functions": 105 | [ 106 | { 107 | "function": "minecraft:enchant_randomly" 108 | } 109 | ] 110 | } 111 | ] 112 | } 113 | ] 114 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/woodland_mansion.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 1.0, 10 | "max": 3.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 5, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 15, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:steel_lock_mechanism", 52 | "weight": 10 53 | } 54 | ] 55 | }, 56 | { 57 | "name": "pool1", 58 | "rolls": 59 | { 60 | "min": 1.0, 61 | "max": 4.0, 62 | "type": "minecraft:uniform" 63 | }, 64 | "entries": 65 | [ 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:iron_lock_pick", 69 | "weight": 5, 70 | "functions": 71 | [ 72 | { 73 | "function": "minecraft:set_count", 74 | "count": 75 | { 76 | "min": 2.0, 77 | "max": 4.0, 78 | "type": "minecraft:uniform" 79 | } 80 | } 81 | ] 82 | }, 83 | { 84 | "type": "minecraft:item", 85 | "name": "locks:gold_lock_pick", 86 | "weight": 5, 87 | "functions": 88 | [ 89 | { 90 | "function": "minecraft:set_count", 91 | "count": 92 | { 93 | "min": 2.0, 94 | "max": 4.0, 95 | "type": "minecraft:uniform" 96 | } 97 | } 98 | ] 99 | }, 100 | { 101 | "type": "minecraft:item", 102 | "name": "locks:iron_lock_mechanism", 103 | "weight": 5 104 | }, 105 | { 106 | "type": "minecraft:item", 107 | "name": "locks:wood_lock_mechanism", 108 | "weight": 10 109 | } 110 | ] 111 | } 112 | ] 113 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/abandoned_mineshaft.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "main", 7 | "rolls": 8 | { 9 | "min": 1.0, 10 | "max": 3.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 5, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 15, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:steel_lock_mechanism", 52 | "weight": 10 53 | } 54 | ] 55 | }, 56 | { 57 | "name": "pool1", 58 | "rolls": 59 | { 60 | "min": 1.0, 61 | "max": 4.0, 62 | "type": "minecraft:uniform" 63 | }, 64 | "entries": 65 | [ 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:iron_lock_pick", 69 | "weight": 5, 70 | "functions": 71 | [ 72 | { 73 | "function": "minecraft:set_count", 74 | "count": 75 | { 76 | "min": 2.0, 77 | "max": 4.0, 78 | "type": "minecraft:uniform" 79 | } 80 | } 81 | ] 82 | }, 83 | { 84 | "type": "minecraft:item", 85 | "name": "locks:gold_lock_pick", 86 | "weight": 5, 87 | "functions": 88 | [ 89 | { 90 | "function": "minecraft:set_count", 91 | "count": 92 | { 93 | "min": 2.0, 94 | "max": 4.0, 95 | "type": "minecraft:uniform" 96 | } 97 | } 98 | ] 99 | }, 100 | { 101 | "type": "minecraft:item", 102 | "name": "locks:iron_lock_mechanism", 103 | "weight": 5 104 | }, 105 | { 106 | "type": "minecraft:item", 107 | "name": "locks:wood_lock_mechanism", 108 | "weight": 10 109 | } 110 | ] 111 | } 112 | ] 113 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/config/LocksServerConfig.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.config; 2 | 3 | import java.util.List; 4 | import java.util.regex.Pattern; 5 | 6 | import com.google.common.collect.Lists; 7 | 8 | import net.minecraft.util.math.BlockPos; 9 | import net.minecraft.world.World; 10 | import net.minecraftforge.common.ForgeConfigSpec; 11 | 12 | public class LocksServerConfig 13 | { 14 | public static final ForgeConfigSpec SPEC; 15 | 16 | public static final ForgeConfigSpec.IntValue MAX_LOCKABLE_VOLUME; 17 | public static final ForgeConfigSpec.ConfigValue> LOCKABLE_BLOCKS; 18 | public static final ForgeConfigSpec.BooleanValue ALLOW_REMOVING_LOCKS; 19 | public static final ForgeConfigSpec.BooleanValue PROTECT_LOCKABLES; 20 | public static final ForgeConfigSpec.BooleanValue EASY_LOCK; 21 | 22 | public static Pattern[] lockableBlocks; 23 | 24 | static 25 | { 26 | ForgeConfigSpec.Builder cfg = new ForgeConfigSpec.Builder(); 27 | 28 | MAX_LOCKABLE_VOLUME = cfg 29 | .comment("Maximum amount of blocks that can be locked at once") 30 | .defineInRange("Max Lockable Volume", 6, 1, Integer.MAX_VALUE); 31 | LOCKABLE_BLOCKS = cfg 32 | .comment("Blocks that can be locked. Each entry is the mod domain followed by the block's registry name. Can include regular expressions") 33 | .defineList("Lockable Blocks", Lists.newArrayList(".*chest", ".*barrel", ".*hopper", ".*door", ".*trapdoor", ".*fence_gate", ".*shulker_box"), e -> e instanceof String); 34 | ALLOW_REMOVING_LOCKS = cfg 35 | .comment("Open locks can be removed with an empty hand while sneaking") 36 | .define("Allow Removing Locks", true); 37 | PROTECT_LOCKABLES = cfg 38 | .comment("Locked blocks cannot be destroyed in survival mode") 39 | .define("Protect Lockables", true); 40 | EASY_LOCK = cfg 41 | .comment("Lock blocks with just one click! It's magic! (Will probably fail spectacularly with custom doors, custom double chests, etc)") 42 | .define("Easy Lock", true); 43 | 44 | SPEC = cfg.build(); 45 | } 46 | 47 | private LocksServerConfig() {} 48 | 49 | public static void init() 50 | { 51 | lockableBlocks = LOCKABLE_BLOCKS.get().stream().map(s -> Pattern.compile(s)).toArray(Pattern[]::new); 52 | } 53 | 54 | public static boolean canLock(World world, BlockPos pos) 55 | { 56 | String name = world.getBlockState(pos).getBlock().getRegistryName().toString(); 57 | for(Pattern p : lockableBlocks) 58 | if(p.matcher(name).matches()) 59 | return true; 60 | return false; 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/resources/data/locks/loot_tables/inject/chests/buried_treasure.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:chest", 3 | "pools": 4 | [ 5 | { 6 | "name": "locks", 7 | "rolls": 8 | { 9 | "min": 0.0, 10 | "max": 3.0, 11 | "type": "minecraft:uniform" 12 | }, 13 | "entries": 14 | [ 15 | { 16 | "type": "minecraft:item", 17 | "name": "locks:diamond_lock_pick", 18 | "weight": 3, 19 | "functions": 20 | [ 21 | { 22 | "function": "minecraft:set_count", 23 | "count": 24 | { 25 | "min": 1.0, 26 | "max": 2.0, 27 | "type": "minecraft:uniform" 28 | } 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "minecraft:item", 34 | "name": "locks:steel_lock_pick", 35 | "weight": 10, 36 | "functions": 37 | [ 38 | { 39 | "function": "minecraft:set_count", 40 | "count": 41 | { 42 | "min": 2.0, 43 | "max": 4.0, 44 | "type": "minecraft:uniform" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "minecraft:item", 51 | "name": "locks:gold_lock_pick", 52 | "weight": 10, 53 | "functions": 54 | [ 55 | { 56 | "function": "minecraft:set_count", 57 | "count": 58 | { 59 | "min": 2.0, 60 | "max": 4.0, 61 | "type": "minecraft:uniform" 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "minecraft:item", 68 | "name": "locks:steel_lock_mechanism", 69 | "weight": 5 70 | }, 71 | { 72 | "type": "minecraft:item", 73 | "name": "locks:iron_lock_mechanism", 74 | "weight": 5 75 | }, 76 | { 77 | "type": "minecraft:item", 78 | "name": "locks:wood_lock_mechanism", 79 | "weight": 10 80 | }, 81 | { 82 | "type": "minecraft:item", 83 | "name": "locks:diamond_lock", 84 | "weight": 1, 85 | "functions": 86 | [ 87 | { 88 | "function": "minecraft:enchant_randomly" 89 | } 90 | ] 91 | }, 92 | { 93 | "type": "minecraft:item", 94 | "name": "locks:steel_lock", 95 | "weight": 2, 96 | "functions": 97 | [ 98 | { 99 | "function": "minecraft:enchant_randomly" 100 | } 101 | ] 102 | }, 103 | { 104 | "type": "minecraft:item", 105 | "name": "locks:gold_lock", 106 | "weight": 5, 107 | "functions": 108 | [ 109 | { 110 | "function": "minecraft:enchant_randomly" 111 | } 112 | ] 113 | } 114 | ] 115 | } 116 | ] 117 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksCapabilities.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.common.capability.CapabilityProvider; 4 | import melonslise.locks.common.capability.CapabilityStorage; 5 | import melonslise.locks.common.capability.EmptyCapabilityStorage; 6 | import melonslise.locks.common.capability.ILockableHandler; 7 | import melonslise.locks.common.capability.ILockableStorage; 8 | import melonslise.locks.common.capability.ISelection; 9 | import melonslise.locks.common.capability.LockableHandler; 10 | import melonslise.locks.common.capability.LockableStorage; 11 | import melonslise.locks.common.capability.Selection; 12 | import melonslise.locks.common.capability.SerializableCapabilityProvider; 13 | import net.minecraft.entity.Entity; 14 | import net.minecraft.entity.player.PlayerEntity; 15 | import net.minecraft.world.World; 16 | import net.minecraft.world.chunk.Chunk; 17 | import net.minecraftforge.common.capabilities.Capability; 18 | import net.minecraftforge.common.capabilities.CapabilityInject; 19 | import net.minecraftforge.common.capabilities.CapabilityManager; 20 | import net.minecraftforge.event.AttachCapabilitiesEvent; 21 | 22 | public final class LocksCapabilities 23 | { 24 | @CapabilityInject(ILockableHandler.class) 25 | public static final Capability LOCKABLE_HANDLER = null; 26 | 27 | @CapabilityInject(ILockableStorage.class) 28 | public static final Capability LOCKABLE_STORAGE = null; 29 | 30 | @CapabilityInject(ISelection.class) 31 | public static final Capability SELECTION = null; 32 | 33 | private LocksCapabilities() {} 34 | 35 | public static void register() 36 | { 37 | CapabilityManager.INSTANCE.register(ILockableHandler.class, new CapabilityStorage(), () -> null); 38 | CapabilityManager.INSTANCE.register(ILockableStorage.class, new CapabilityStorage(), () -> null); 39 | CapabilityManager.INSTANCE.register(ISelection.class, new EmptyCapabilityStorage(), Selection::new); 40 | } 41 | 42 | public static void attachToWorld(AttachCapabilitiesEvent e) 43 | { 44 | e.addCapability(LockableHandler.ID, new SerializableCapabilityProvider(LOCKABLE_HANDLER, new LockableHandler(e.getObject()))); 45 | } 46 | 47 | public static void attachToChunk(AttachCapabilitiesEvent e) 48 | { 49 | e.addCapability(LockableStorage.ID, new SerializableCapabilityProvider(LOCKABLE_STORAGE, new LockableStorage(e.getObject()))); 50 | } 51 | 52 | public static void attachToEntity(AttachCapabilitiesEvent e) 53 | { 54 | if(e.getObject() instanceof PlayerEntity) 55 | e.addCapability(Selection.ID, new CapabilityProvider(SELECTION, new Selection())); 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/client/gui/sprite/Sprite.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.client.gui.sprite; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Queue; 5 | 6 | import com.mojang.blaze3d.matrix.MatrixStack; 7 | 8 | import melonslise.locks.client.gui.sprite.action.IAction; 9 | import melonslise.locks.client.util.LocksClientUtil; 10 | import net.minecraft.util.math.vector.Vector3f; 11 | import net.minecraftforge.api.distmarker.Dist; 12 | import net.minecraftforge.api.distmarker.OnlyIn; 13 | 14 | @OnlyIn(Dist.CLIENT) 15 | public class Sprite 16 | { 17 | private Queue actions = new ArrayDeque<>(4); 18 | public TextureInfo tex; 19 | public float posX , posY, oldPosX, oldPosY, speedX, speedY, rot, oldRot, rotSpeed, originX, originY, alpha = 1f, oldAlpha = 1f; 20 | 21 | public Sprite(TextureInfo tex) 22 | { 23 | this.tex = tex; 24 | } 25 | 26 | public Sprite position(float posX, float posY) 27 | { 28 | this.posX = this.oldPosX = posX; 29 | this.posY = this.oldPosY = posY; 30 | return this; 31 | } 32 | 33 | public Sprite rotation(float rot, float originX, float originY) 34 | { 35 | this.rot = this.oldRot = rot; 36 | this.originX = originX; 37 | this.originY = originY; 38 | return this; 39 | } 40 | 41 | public Sprite alpha(float alpha) 42 | { 43 | this.alpha = this.oldAlpha = alpha; 44 | return this; 45 | } 46 | 47 | public void execute(IAction... actions) 48 | { 49 | for(IAction action : actions) 50 | this.actions.offer(action); 51 | } 52 | 53 | public boolean isExecuting() 54 | { 55 | return !this.actions.isEmpty(); 56 | } 57 | 58 | // FIXME new quat obj every frame? JFC 59 | public void draw(MatrixStack mtx, float partialTick) 60 | { 61 | if(this.alpha <= 0f) 62 | return; 63 | mtx.pushPose(); 64 | mtx.translate(this.originX, this.originY, 0f); 65 | mtx.mulPose(Vector3f.ZP.rotationDegrees(LocksClientUtil.lerp(this.oldRot, this.rot, partialTick))); 66 | mtx.translate(-this.originX, -this.originY, 0f); 67 | this.tex.draw(mtx, LocksClientUtil.lerp(this.oldPosX, this.posX, partialTick), LocksClientUtil.lerp(this.oldPosY, this.posY, partialTick), LocksClientUtil.lerp(this.oldAlpha, this.alpha, partialTick)); 68 | mtx.popPose(); 69 | } 70 | 71 | public void update() 72 | { 73 | this.oldPosX = this.posX; 74 | this.oldPosY = this.posY; 75 | this.oldRot = this.rot; 76 | this.oldAlpha = this.alpha; 77 | this.posX += this.speedX; 78 | this.posY += this.speedY; 79 | this.rot += this.rotSpeed; 80 | IAction action = this.actions.peek(); 81 | if(action == null) 82 | return; 83 | if(action.isFinished(this)) 84 | this.actions.poll().finish(this); 85 | else 86 | action.update(this); 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/recipe/KeyRecipe.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.recipe; 2 | 3 | import melonslise.locks.common.init.LocksItems; 4 | import melonslise.locks.common.init.LocksRecipeSerializers; 5 | import melonslise.locks.common.item.LockingItem; 6 | import net.minecraft.inventory.CraftingInventory; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.item.WrittenBookItem; 9 | import net.minecraft.item.crafting.IRecipeSerializer; 10 | import net.minecraft.item.crafting.SpecialRecipe; 11 | import net.minecraft.util.NonNullList; 12 | import net.minecraft.util.ResourceLocation; 13 | import net.minecraft.world.World; 14 | 15 | public class KeyRecipe extends SpecialRecipe 16 | { 17 | public KeyRecipe(ResourceLocation id) 18 | { 19 | super(id); 20 | } 21 | 22 | @Override 23 | public IRecipeSerializer getSerializer() 24 | { 25 | return LocksRecipeSerializers.KEY.get(); 26 | } 27 | 28 | @Override 29 | public boolean matches(CraftingInventory inv, World world) 30 | { 31 | boolean hasLocking = false; 32 | int blanks = 0; 33 | 34 | for(int a = 0; a < inv.getContainerSize(); ++a) 35 | { 36 | ItemStack stack = inv.getItem(a); 37 | if(stack.isEmpty()) 38 | continue; 39 | if(stack.hasTag() && stack.getTag().contains(LockingItem.KEY_ID)) 40 | { 41 | if(hasLocking) 42 | return false; 43 | hasLocking = true; 44 | } 45 | else if(stack.getItem() == LocksItems.KEY_BLANK.get()) 46 | ++blanks; 47 | else 48 | return false; 49 | } 50 | return hasLocking && blanks >= 1; 51 | } 52 | 53 | @Override 54 | public ItemStack assemble(CraftingInventory inv) 55 | { 56 | ItemStack locking = ItemStack.EMPTY; 57 | int blanks = 0; 58 | 59 | for (int a = 0; a < inv.getContainerSize(); ++a) 60 | { 61 | ItemStack stack = inv.getItem(a); 62 | if (stack.isEmpty()) 63 | continue; 64 | if (stack.hasTag() && stack.getTag().contains(LockingItem.KEY_ID)) 65 | { 66 | if (!locking.isEmpty()) 67 | return ItemStack.EMPTY; 68 | locking = stack; 69 | } 70 | else if(stack.getItem() == LocksItems.KEY_BLANK.get()) 71 | ++blanks; 72 | else 73 | return ItemStack.EMPTY; 74 | } 75 | 76 | if(!locking.isEmpty() && blanks >= 1) 77 | return LockingItem.copyId(locking, new ItemStack(LocksItems.KEY.get())); 78 | return ItemStack.EMPTY; 79 | } 80 | 81 | @Override 82 | public NonNullList getRemainingItems(CraftingInventory inv) 83 | { 84 | NonNullList list = NonNullList.withSize(inv.getContainerSize(), ItemStack.EMPTY); 85 | 86 | for (int a = 0; a < list.size(); ++a) 87 | { 88 | ItemStack stack = inv.getItem(a); 89 | if(!stack.hasTag() || !stack.getTag().contains(LockingItem.KEY_ID)) 90 | continue; 91 | list.set(a, stack.copy()); 92 | break; 93 | } 94 | 95 | return list; 96 | } 97 | 98 | @Override 99 | public boolean canCraftInDimensions(int x, int y) 100 | { 101 | return x >= 3 && y >= 3; 102 | } 103 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/init/LocksItems.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.init; 2 | 3 | import melonslise.locks.Locks; 4 | import melonslise.locks.common.item.KeyItem; 5 | import melonslise.locks.common.item.KeyRingItem; 6 | import melonslise.locks.common.item.LockItem; 7 | import melonslise.locks.common.item.LockPickItem; 8 | import melonslise.locks.common.item.MasterKeyItem; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.item.ItemGroup; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraftforge.api.distmarker.Dist; 13 | import net.minecraftforge.api.distmarker.OnlyIn; 14 | import net.minecraftforge.fml.RegistryObject; 15 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 16 | import net.minecraftforge.registries.DeferredRegister; 17 | import net.minecraftforge.registries.ForgeRegistries; 18 | 19 | public final class LocksItems 20 | { 21 | public static final ItemGroup TAB = new ItemGroup(Locks.ID) 22 | { 23 | @OnlyIn(Dist.CLIENT) 24 | @Override 25 | public ItemStack makeIcon() 26 | { 27 | return new ItemStack(LocksItems.IRON_LOCK.get()); 28 | } 29 | }.setEnchantmentCategories(LocksEnchantments.LOCK_TYPE); 30 | 31 | public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Locks.ID); 32 | 33 | public static final RegistryObject 34 | SPRING = add("spring", new Item(new Item.Properties().tab(TAB))), 35 | WOOD_LOCK_MECHANISM = add("wood_lock_mechanism", new Item(new Item.Properties().tab(TAB))), 36 | IRON_LOCK_MECHANISM = add("iron_lock_mechanism", new Item(new Item.Properties().tab(TAB))), 37 | STEEL_LOCK_MECHANISM = add("steel_lock_mechanism", new Item(new Item.Properties().tab(TAB))), 38 | KEY_BLANK = add("key_blank", new Item(new Item.Properties().tab(TAB))), 39 | WOOD_LOCK = add("wood_lock", new LockItem(5, 15, 4, new Item.Properties().tab(TAB))), 40 | IRON_LOCK = add("iron_lock", new LockItem(7, 14, 12, new Item.Properties().tab(TAB))), 41 | STEEL_LOCK = add("steel_lock", new LockItem(9, 12, 20, new Item.Properties().tab(TAB))), 42 | GOLD_LOCK = add("gold_lock", new LockItem(6, 22, 6, new Item.Properties().tab(TAB))), 43 | DIAMOND_LOCK = add("diamond_lock", new LockItem(11, 10, 100, new Item.Properties().tab(TAB))), 44 | KEY = add("key", new KeyItem(new Item.Properties().tab(TAB))), 45 | MASTER_KEY = add("master_key", new MasterKeyItem(new Item.Properties().tab(TAB))), 46 | KEY_RING = add("key_ring", new KeyRingItem(1, new Item.Properties().tab(TAB))), 47 | WOOD_LOCK_PICK = add("wood_lock_pick", new LockPickItem(0.2f, new Item.Properties().tab(TAB))), 48 | IRON_LOCK_PICK = add("iron_lock_pick", new LockPickItem(0.35f, new Item.Properties().tab(TAB))), 49 | STEEL_LOCK_PICK = add("steel_lock_pick", new LockPickItem(0.7f, new Item.Properties().tab(TAB))), 50 | GOLD_LOCK_PICK = add("gold_lock_pick", new LockPickItem(0.25f, new Item.Properties().tab(TAB))), 51 | DIAMOND_LOCK_PICK = add("diamond_lock_pick", new LockPickItem(0.85f, new Item.Properties().tab(TAB))); 52 | 53 | private LocksItems() {} 54 | 55 | public static void register() 56 | { 57 | ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); 58 | } 59 | 60 | public static RegistryObject add(String name, Item item) 61 | { 62 | return ITEMS.register(name, () -> item); 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/Lock.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import java.util.Observable; 4 | import java.util.Random; 5 | 6 | import melonslise.locks.common.item.LockItem; 7 | import melonslise.locks.common.item.LockingItem; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.nbt.CompoundNBT; 10 | import net.minecraft.network.PacketBuffer; 11 | 12 | public class Lock extends Observable 13 | { 14 | public final int id; 15 | // index is the order, value is the pin number 16 | protected final byte[] combo; 17 | protected boolean locked; 18 | 19 | // TODO if lock is reshuffled any time other than during creation, then next time it is loaded it will have the initial combination and not the newly reshuffled one. Thankfully reshuffling like that does happen, but this should be changed if it does happen 20 | public final Random rng; 21 | 22 | public Lock(int id, int length, boolean locked) 23 | { 24 | this.id = id; 25 | this.rng = new Random(id); 26 | this.combo = this.shuffle(length); 27 | // this.lookup = this.inverse(this.combo); 28 | this.locked = locked; 29 | } 30 | 31 | public static Lock from(ItemStack stack) 32 | { 33 | return new Lock(LockingItem.getOrSetId(stack), LockItem.getOrSetLength(stack), !LockItem.isOpen(stack)); 34 | } 35 | 36 | public static final String KEY_ID = "Id", KEY_LENGTH = "Length", KEY_LOCKED = "Locked"; 37 | 38 | public static Lock fromNbt(CompoundNBT nbt) 39 | { 40 | return new Lock(nbt.getInt(KEY_ID), nbt.getByte(KEY_LENGTH), nbt.getBoolean(KEY_LOCKED)); 41 | } 42 | 43 | public static CompoundNBT toNbt(Lock lock) 44 | { 45 | CompoundNBT nbt = new CompoundNBT(); 46 | nbt.putInt(KEY_ID, lock.id); 47 | nbt.putByte(KEY_LENGTH, (byte) lock.combo.length); 48 | nbt.putBoolean(KEY_LOCKED, lock.locked); 49 | return nbt; 50 | } 51 | 52 | public static Lock fromBuf(PacketBuffer buf) 53 | { 54 | return new Lock(buf.readInt(), (int) buf.readByte(), buf.readBoolean()); 55 | } 56 | 57 | public static void toBuf(PacketBuffer buf, Lock lock) 58 | { 59 | buf.writeInt(lock.id); 60 | buf.writeByte((int) lock.getLength()); 61 | buf.writeBoolean(lock.isLocked()); 62 | } 63 | 64 | public byte[] shuffle(int length) 65 | { 66 | byte[] combo = new byte[length]; 67 | for(byte a = 0; a < length; ++a) 68 | combo[a] = a; 69 | LocksUtil.shuffle(combo, this.rng); 70 | return combo; 71 | } 72 | 73 | /* 74 | public byte[] inverse(byte[] combination) 75 | { 76 | byte[] lookup = new byte[combination.length]; 77 | for(byte a = 0; a < combination.length; ++a) 78 | lookup[combination[a]] = a; 79 | return lookup; 80 | } 81 | */ 82 | 83 | public int getLength() 84 | { 85 | return this.combo.length; 86 | } 87 | 88 | public boolean isLocked() 89 | { 90 | return this.locked; 91 | } 92 | 93 | public void setLocked(boolean locked) 94 | { 95 | if(this.locked == locked) 96 | return; 97 | this.locked = locked; 98 | this.setChanged(); 99 | this.notifyObservers(); 100 | } 101 | 102 | public int getPin(int index) 103 | { 104 | return this.combo[index]; 105 | } 106 | 107 | public boolean checkPin(int index, int pin) 108 | { 109 | return this.getPin(index) == pin; 110 | } 111 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/item/KeyRingItem.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.item; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | import melonslise.locks.common.capability.CapabilityProvider; 7 | import melonslise.locks.common.capability.KeyRingInventory; 8 | import melonslise.locks.common.container.KeyRingContainer; 9 | import melonslise.locks.common.init.LocksSoundEvents; 10 | import melonslise.locks.common.util.Lockable; 11 | import melonslise.locks.common.util.LocksUtil; 12 | import net.minecraft.entity.player.PlayerEntity; 13 | import net.minecraft.entity.player.ServerPlayerEntity; 14 | import net.minecraft.item.Item; 15 | import net.minecraft.item.ItemStack; 16 | import net.minecraft.item.ItemUseContext; 17 | import net.minecraft.nbt.CompoundNBT; 18 | import net.minecraft.util.ActionResult; 19 | import net.minecraft.util.ActionResultType; 20 | import net.minecraft.util.Hand; 21 | import net.minecraft.util.SoundCategory; 22 | import net.minecraft.util.math.BlockPos; 23 | import net.minecraft.world.World; 24 | import net.minecraftforge.common.capabilities.ICapabilityProvider; 25 | import net.minecraftforge.fml.network.NetworkHooks; 26 | import net.minecraftforge.items.CapabilityItemHandler; 27 | import net.minecraftforge.items.IItemHandler; 28 | 29 | public class KeyRingItem extends Item 30 | { 31 | public final int rows; 32 | 33 | public KeyRingItem(int rows, Properties props) 34 | { 35 | super(props.stacksTo(1)); 36 | this.rows = rows; 37 | } 38 | 39 | @Override 40 | public ICapabilityProvider initCapabilities(ItemStack stack, CompoundNBT nbt) 41 | { 42 | return new CapabilityProvider(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, new KeyRingInventory(stack, this.rows, 9)); 43 | } 44 | 45 | public static boolean containsId(ItemStack stack, int id) 46 | { 47 | IItemHandler inv = stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null); 48 | for(int a = 0; a < inv.getSlots(); ++a) 49 | if(LockingItem.getOrSetId(inv.getStackInSlot(a)) == id) 50 | return true; 51 | return false; 52 | } 53 | 54 | @Override 55 | public ActionResult use(World world, PlayerEntity player, Hand hand) 56 | { 57 | ItemStack stack = player.getItemInHand(hand); 58 | if(!player.level.isClientSide) 59 | NetworkHooks.openGui((ServerPlayerEntity) player, new KeyRingContainer.Provider(stack), new KeyRingContainer.Writer(hand)); 60 | return new ActionResult<>(ActionResultType.PASS, stack); 61 | } 62 | 63 | @Override 64 | public ActionResultType useOn(ItemUseContext ctx) 65 | { 66 | World world = ctx.getLevel(); 67 | BlockPos pos = ctx.getClickedPos(); 68 | IItemHandler inv = ctx.getItemInHand().getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null); 69 | List intersect = LocksUtil.intersecting(world, pos).collect(Collectors.toList()); 70 | if(intersect.isEmpty()) 71 | return ActionResultType.PASS; 72 | for(int a = 0; a < inv.getSlots(); ++a) 73 | { 74 | int id = LockingItem.getOrSetId(inv.getStackInSlot(a)); 75 | List match = intersect.stream().filter(lkb -> lkb.lock.id == id).collect(Collectors.toList()); 76 | if(match.isEmpty()) 77 | continue; 78 | world.playSound(ctx.getPlayer(), pos, LocksSoundEvents.LOCK_OPEN.get(), SoundCategory.BLOCKS, 1f, 1f); 79 | if(world.isClientSide) 80 | return ActionResultType.SUCCESS; 81 | for(Lockable lkb : match) 82 | lkb.lock.setLocked(!lkb.lock.isLocked()); 83 | return ActionResultType.SUCCESS; 84 | } 85 | return ActionResultType.SUCCESS; 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/config/LocksConfig.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.config; 2 | 3 | import java.util.List; 4 | import java.util.NavigableMap; 5 | import java.util.Random; 6 | import java.util.TreeMap; 7 | 8 | import com.google.common.collect.Lists; 9 | 10 | import melonslise.locks.common.util.LocksUtil; 11 | import net.minecraft.enchantment.EnchantmentHelper; 12 | import net.minecraft.item.Item; 13 | import net.minecraft.item.ItemStack; 14 | import net.minecraft.util.ResourceLocation; 15 | import net.minecraftforge.common.ForgeConfigSpec; 16 | import net.minecraftforge.registries.ForgeRegistries; 17 | 18 | public final class LocksConfig 19 | { 20 | public static final ForgeConfigSpec SPEC; 21 | 22 | public static final ForgeConfigSpec.DoubleValue GENERATION_CHANCE; 23 | public static final ForgeConfigSpec.DoubleValue GENERATION_ENCHANT_CHANCE; 24 | public static final ForgeConfigSpec.ConfigValue> GENERATED_LOCKS; 25 | public static final ForgeConfigSpec.ConfigValue> GENERATED_LOCK_WEIGHTS; 26 | public static final ForgeConfigSpec.BooleanValue RANDOMIZE_LOADED_LOCKS; 27 | 28 | public static NavigableMap weightedGeneratedLocks; 29 | public static int weightTotal; 30 | 31 | static 32 | { 33 | ForgeConfigSpec.Builder cfg = new ForgeConfigSpec.Builder(); 34 | 35 | GENERATION_CHANCE = cfg 36 | .comment("Chance to generate a random lock on every new chest during world generation. Set to 0 to disable") 37 | .defineInRange("Generation Chance", 0.85d, 0d, 1d); 38 | GENERATION_ENCHANT_CHANCE = cfg 39 | .comment("Chance to randomly enchant a generated lock during world generation. Set to 0 to disable") 40 | .defineInRange("Generation Enchant Chance", 0.4d, 0d, 1d); 41 | GENERATED_LOCKS = cfg 42 | .comment("Items that can be generated as locks (must be instance of LockItem in code!)") 43 | .defineList("Generated Locks", Lists.newArrayList("locks:wood_lock", "locks:iron_lock", "locks:steel_lock", "locks:gold_lock", "locks:diamond_lock"), e -> e instanceof String); 44 | GENERATED_LOCK_WEIGHTS= cfg 45 | .comment("WARNING: THE AMOUNT OF NUMBERS SHOULD BE EQUAL TO THE AMOUNT OF GENERATED LOCK ITEMS!!!", "The relative probability that the corresponding lock item will be generated on a chest. Higher number = higher chance to generate") 46 | .defineList("Generated Lock Chances", Lists.newArrayList(3, 3, 2, 2, 1), e -> e instanceof Integer); 47 | RANDOMIZE_LOADED_LOCKS = cfg 48 | .comment("Randomize lock IDs and combinations when loading them from a structure file. Randomization works just like during world generation") 49 | .define("Randomize Loaded Locks", false); 50 | 51 | SPEC = cfg.build(); 52 | } 53 | 54 | private LocksConfig() {} 55 | 56 | // https://gist.github.com/raws/1667807 57 | public static void init() 58 | { 59 | weightedGeneratedLocks = new TreeMap<>(); 60 | weightTotal = 0; 61 | List locks = GENERATED_LOCKS.get(); 62 | List weights = GENERATED_LOCK_WEIGHTS.get(); 63 | for(int a = 0; a < locks.size(); ++a) 64 | { 65 | weightTotal += weights.get(a); 66 | weightedGeneratedLocks.put(weightTotal, ForgeRegistries.ITEMS.getValue(new ResourceLocation(locks.get(a)))); 67 | } 68 | } 69 | 70 | public static boolean canGen(Random rng) 71 | { 72 | return LocksUtil.chance(rng, GENERATION_CHANCE.get()); 73 | } 74 | 75 | public static boolean canEnchant(Random rng) 76 | { 77 | return LocksUtil.chance(rng, GENERATION_ENCHANT_CHANCE.get()); 78 | } 79 | 80 | public static ItemStack getRandomLock(Random rng) 81 | { 82 | ItemStack stack = new ItemStack(weightedGeneratedLocks.ceilingEntry(rng.nextInt(weightTotal) + 1).getValue()); 83 | return canEnchant(rng) ? EnchantmentHelper.enchantItem(rng, stack, 5 + rng.nextInt(30), false) : stack; 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/item/LockPickItem.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.item; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | import melonslise.locks.Locks; 7 | import melonslise.locks.common.container.LockPickingContainer; 8 | import melonslise.locks.common.init.LocksEnchantments; 9 | import melonslise.locks.common.util.Lockable; 10 | import melonslise.locks.common.util.LocksPredicates; 11 | import melonslise.locks.common.util.LocksUtil; 12 | import net.minecraft.client.util.ITooltipFlag; 13 | import net.minecraft.enchantment.EnchantmentHelper; 14 | import net.minecraft.entity.player.PlayerEntity; 15 | import net.minecraft.entity.player.ServerPlayerEntity; 16 | import net.minecraft.item.Item; 17 | import net.minecraft.item.ItemStack; 18 | import net.minecraft.item.ItemUseContext; 19 | import net.minecraft.nbt.CompoundNBT; 20 | import net.minecraft.util.ActionResultType; 21 | import net.minecraft.util.Hand; 22 | import net.minecraft.util.math.BlockPos; 23 | import net.minecraft.util.text.ITextComponent; 24 | import net.minecraft.util.text.TextFormatting; 25 | import net.minecraft.util.text.TranslationTextComponent; 26 | import net.minecraft.world.World; 27 | import net.minecraftforge.api.distmarker.Dist; 28 | import net.minecraftforge.api.distmarker.OnlyIn; 29 | import net.minecraftforge.fml.network.NetworkHooks; 30 | 31 | public class LockPickItem extends Item 32 | { 33 | public static final ITextComponent TOO_COMPLEX_MESSAGE = new TranslationTextComponent(Locks.ID + ".status.too_complex"); 34 | 35 | public final float strength; 36 | 37 | public LockPickItem(float strength, Properties props) 38 | { 39 | super(props); 40 | this.strength = strength; 41 | } 42 | 43 | public static final String KEY_STRENGTH = "Strength"; 44 | 45 | // WARNING: EXPECTS LOCKPICKITEM STACK 46 | public static float getOrSetStrength(ItemStack stack) 47 | { 48 | CompoundNBT nbt = stack.getOrCreateTag(); 49 | if(!nbt.contains(KEY_STRENGTH)) 50 | nbt.putFloat(KEY_STRENGTH, ((LockPickItem) stack.getItem()).strength); 51 | return nbt.getFloat(KEY_STRENGTH); 52 | } 53 | 54 | public static boolean canPick(ItemStack stack, int cmp) 55 | { 56 | return getOrSetStrength(stack) > cmp * 0.25f; 57 | } 58 | 59 | public static boolean canPick(ItemStack stack, Lockable lkb) 60 | { 61 | return canPick(stack, EnchantmentHelper.getItemEnchantmentLevel(LocksEnchantments.COMPLEXITY.get(), lkb.stack)); 62 | } 63 | 64 | @Override 65 | public ActionResultType useOn(ItemUseContext ctx) 66 | { 67 | World world = ctx.getLevel(); 68 | PlayerEntity player = ctx.getPlayer(); 69 | BlockPos pos = ctx.getClickedPos(); 70 | List match = LocksUtil.intersecting(world, pos).filter(LocksPredicates.LOCKED).collect(Collectors.toList()); 71 | if(match.isEmpty()) 72 | return ActionResultType.PASS; 73 | Lockable lkb = match.get(0); 74 | if(!canPick(ctx.getItemInHand(), lkb)) 75 | { 76 | if(world.isClientSide) 77 | player.displayClientMessage(TOO_COMPLEX_MESSAGE, true); 78 | return ActionResultType.PASS; 79 | } 80 | if(world.isClientSide) 81 | return ActionResultType.SUCCESS; 82 | Hand hand = ctx.getHand(); 83 | NetworkHooks.openGui((ServerPlayerEntity) player, new LockPickingContainer.Provider(hand, lkb), new LockPickingContainer.Writer(hand, lkb)); 84 | return ActionResultType.SUCCESS; 85 | } 86 | 87 | @OnlyIn(Dist.CLIENT) 88 | @Override 89 | public void appendHoverText(ItemStack stack, World world, List lines, ITooltipFlag flag) 90 | { 91 | super.appendHoverText(stack, world, lines, flag); 92 | lines.add(new TranslationTextComponent(Locks.ID + ".tooltip.strength", ItemStack.ATTRIBUTE_MODIFIER_FORMAT.format(stack.hasTag() && stack.getTag().contains(KEY_STRENGTH) ? stack.getTag().getFloat(KEY_STRENGTH) : this.strength)).withStyle(TextFormatting.DARK_GREEN)); 93 | } 94 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/KeyRingInventory.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | import melonslise.locks.common.init.LocksItemTags; 6 | import net.minecraft.item.ItemStack; 7 | import net.minecraft.nbt.CompoundNBT; 8 | import net.minecraft.nbt.ListNBT; 9 | import net.minecraftforge.common.util.Constants; 10 | import net.minecraftforge.items.IItemHandlerModifiable; 11 | import net.minecraftforge.items.ItemHandlerHelper; 12 | 13 | // Thanks to Gigaherz 14 | public class KeyRingInventory implements IItemHandlerModifiable 15 | { 16 | public final int size; 17 | public final ItemStack stack; 18 | 19 | public KeyRingInventory(ItemStack stack, int rows, int col) 20 | { 21 | this.size = rows * col; 22 | this.stack = stack; 23 | } 24 | 25 | @Override 26 | public int getSlots() 27 | { 28 | return this.size; 29 | } 30 | 31 | @Override 32 | public ItemStack getStackInSlot(int slot) 33 | { 34 | this.validateSlotIndex(slot); 35 | ListNBT list = this.stack.getOrCreateTag().getList("Items", Constants.NBT.TAG_COMPOUND); 36 | for(int a = 0; a < list.size(); a++) 37 | { 38 | CompoundNBT nbt = list.getCompound(a); 39 | if(nbt.getInt("Slot") != slot) 40 | continue; 41 | return ItemStack.of(nbt); 42 | } 43 | return ItemStack.EMPTY; 44 | } 45 | 46 | @Override 47 | public void setStackInSlot(int slot, ItemStack stack) 48 | { 49 | this.validateSlotIndex(slot); 50 | CompoundNBT nbt = null; 51 | if(!stack.isEmpty()) 52 | { 53 | nbt = new CompoundNBT(); 54 | nbt.putInt("Slot", slot); 55 | stack.save(nbt); 56 | } 57 | ListNBT list = this.stack.getOrCreateTag().getList("Items", Constants.NBT.TAG_COMPOUND); 58 | for(int a = 0; a < list.size(); a++) 59 | { 60 | CompoundNBT existing = list.getCompound(a); 61 | if(existing.getInt("Slot") != slot) 62 | continue; 63 | if(!stack.isEmpty()) 64 | list.set(a, nbt); 65 | else 66 | list.remove(a); 67 | return; 68 | } 69 | if(!stack.isEmpty()) 70 | list.add(nbt); 71 | this.stack.getOrCreateTag().put("Items", list); 72 | } 73 | 74 | @Override 75 | public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) 76 | { 77 | if (stack.isEmpty()) 78 | return ItemStack.EMPTY; 79 | this.validateSlotIndex(slot); 80 | ItemStack existing = getStackInSlot(slot); 81 | int limit = stack.getMaxStackSize(); 82 | if (!existing.isEmpty()) 83 | { 84 | if (!ItemHandlerHelper.canItemStacksStack(stack, existing)) 85 | return stack; 86 | limit -= existing.getCount(); 87 | } 88 | if (limit <= 0) 89 | return stack; 90 | boolean reachedLimit = stack.getCount() > limit; 91 | if (!simulate) 92 | { 93 | if (existing.getCount() <= 0) 94 | existing = reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack; 95 | else 96 | existing.grow(reachedLimit ? limit : stack.getCount()); 97 | this.setStackInSlot(slot, existing); 98 | } 99 | return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.getCount() - limit) : ItemStack.EMPTY; 100 | } 101 | 102 | @Override 103 | public ItemStack extractItem(int slot, int amount, boolean simulate) 104 | { 105 | if (amount == 0) 106 | return ItemStack.EMPTY; 107 | this.validateSlotIndex(slot); 108 | ItemStack existing = this.getStackInSlot(slot); 109 | if (existing.isEmpty()) 110 | return ItemStack.EMPTY; 111 | int toExtract = Math.min(amount, existing.getMaxStackSize()); 112 | if (existing.getCount() <= toExtract) 113 | { 114 | if (!simulate) 115 | this.setStackInSlot(slot, ItemStack.EMPTY); 116 | return existing; 117 | } 118 | else 119 | { 120 | if (!simulate) 121 | this.setStackInSlot(slot, ItemHandlerHelper.copyStackWithSize(existing, existing.getCount() - toExtract)); 122 | return ItemHandlerHelper.copyStackWithSize(existing, toExtract); 123 | } 124 | } 125 | 126 | @Override 127 | public int getSlotLimit(int slot) 128 | { 129 | return 64; 130 | } 131 | 132 | private void validateSlotIndex(int slot) 133 | { 134 | if (slot < 0 || slot >= this.getSlots()) 135 | throw new RuntimeException("Slot " + slot + " not in valid range - [0," + getSlots() + ")"); 136 | } 137 | 138 | @Override 139 | public boolean isItemValid(int slot, @Nonnull ItemStack stack) 140 | { 141 | return stack.getItem().is(LocksItemTags.KEYS); 142 | } 143 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/capability/LockableHandler.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.capability; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import java.util.Observable; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; 9 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 10 | import melonslise.locks.Locks; 11 | import melonslise.locks.common.config.LocksServerConfig; 12 | import melonslise.locks.common.init.LocksCapabilities; 13 | import melonslise.locks.common.init.LocksNetwork; 14 | import melonslise.locks.common.init.LocksPacketDistributors; 15 | import melonslise.locks.common.network.toclient.AddLockablePacket; 16 | import melonslise.locks.common.network.toclient.RemoveLockablePacket; 17 | import melonslise.locks.common.network.toclient.UpdateLockablePacket; 18 | import melonslise.locks.common.util.Lockable; 19 | import net.minecraft.nbt.IntNBT; 20 | import net.minecraft.util.ResourceLocation; 21 | import net.minecraft.util.math.BlockPos; 22 | import net.minecraft.world.World; 23 | import net.minecraft.world.chunk.Chunk; 24 | 25 | /* 26 | * Manages and handles logic for all LOADED lockables by accessing internal ILockableStorage objects. 27 | * This means that there is no way of getting a list of ALL lockables in a world like before 28 | */ 29 | public class LockableHandler implements ILockableHandler 30 | { 31 | public static final ResourceLocation ID = new ResourceLocation(Locks.ID, "lockable_handler"); 32 | 33 | public final World world; 34 | 35 | public AtomicInteger lastId = new AtomicInteger(); 36 | 37 | public Int2ObjectMap lockables = new Int2ObjectLinkedOpenHashMap(); 38 | 39 | public LockableHandler(World world) 40 | { 41 | this.world = world; 42 | } 43 | 44 | public int nextId() 45 | { 46 | return this.lastId.incrementAndGet(); 47 | } 48 | 49 | @Override 50 | public Int2ObjectMap getLoaded() 51 | { 52 | return this.lockables; 53 | } 54 | 55 | @Override 56 | public Int2ObjectMap getInChunk(BlockPos pos) 57 | { 58 | return this.world.hasChunkAt(pos) ? this.world.getChunkAt(pos).getCapability(LocksCapabilities.LOCKABLE_STORAGE).orElse(null).get() : null; 59 | } 60 | 61 | @Override 62 | public boolean add(Lockable lkb) 63 | { 64 | if(lkb.bb.volume() > LocksServerConfig.MAX_LOCKABLE_VOLUME.get()) 65 | return false; 66 | List sts = lkb.bb.containedChunksTo((x, z) -> 67 | { 68 | if(!this.world.hasChunk(x, z)) 69 | return null; 70 | ILockableStorage st = this.world.getChunk(x, z).getCapability(LocksCapabilities.LOCKABLE_STORAGE).orElse(null); 71 | return st.get().values().stream().anyMatch(lkb1 -> lkb1.bb.intersects(lkb.bb)) ? null : st; 72 | }, true); 73 | if(sts == null) 74 | return false; 75 | 76 | // Add to chunk 77 | for(int a = 0; a < sts.size(); ++a) 78 | sts.get(a).add(lkb); 79 | // Add to world 80 | this.lockables.put(lkb.id, lkb); 81 | lkb.addObserver(this); 82 | // Do client/server extras 83 | if(this.world.isClientSide) 84 | lkb.swing(10); 85 | else 86 | LocksNetwork.MAIN.send(LocksPacketDistributors.TRACKING_AREA.with(() -> sts.stream().map(st -> ((LockableStorage) st).chunk)), new AddLockablePacket(lkb)); 87 | return true; 88 | } 89 | 90 | @Override 91 | public boolean remove(int id) 92 | { 93 | Lockable lkb = this.lockables.get(id); 94 | if(lkb == this.lockables.defaultReturnValue()) 95 | return false; 96 | List chs = lkb.bb.containedChunksTo((x, z) -> this.world.hasChunk(x, z) ? this.world.getChunk(x, z) : null, true); 97 | 98 | // Remove from chunk 99 | for(int a = 0; a < chs.size(); ++a) 100 | chs.get(a).getCapability(LocksCapabilities.LOCKABLE_STORAGE).orElse(null).remove(id); 101 | // Remove from world 102 | this.lockables.remove(id); 103 | lkb.deleteObserver(this); 104 | // Do client/server extras 105 | if(this.world.isClientSide) 106 | return true; 107 | LocksNetwork.MAIN.send(LocksPacketDistributors.TRACKING_AREA.with(() -> chs.stream()), new RemoveLockablePacket(id)); 108 | return true; 109 | } 110 | 111 | @Override 112 | public void update(Observable o, Object arg) 113 | { 114 | if(this.world.isClientSide || !(o instanceof Lockable)) 115 | return; 116 | Lockable lockable = (Lockable) o; 117 | LocksNetwork.MAIN.send(LocksPacketDistributors.TRACKING_AREA.with(() -> lockable.bb.containedChunksTo((x, z) -> this.world.hasChunk(x, z) ? this.world.getChunk(x, z) : null, false).stream().filter(Objects::nonNull)), new UpdateLockablePacket(lockable)); 118 | } 119 | 120 | @Override 121 | public IntNBT serializeNBT() 122 | { 123 | return IntNBT.valueOf(this.lastId.get()); 124 | } 125 | 126 | @Override 127 | public void deserializeNBT(IntNBT nbt) 128 | { 129 | this.lastId.set(nbt.getAsInt()); 130 | } 131 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/container/KeyRingContainer.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.container; 2 | 3 | import java.util.function.Consumer; 4 | 5 | import melonslise.locks.common.init.LocksContainerTypes; 6 | import melonslise.locks.common.init.LocksSoundEvents; 7 | import net.minecraft.entity.player.PlayerEntity; 8 | import net.minecraft.entity.player.PlayerInventory; 9 | import net.minecraft.inventory.container.Container; 10 | import net.minecraft.inventory.container.INamedContainerProvider; 11 | import net.minecraft.inventory.container.Slot; 12 | import net.minecraft.item.ItemStack; 13 | import net.minecraft.network.PacketBuffer; 14 | import net.minecraft.util.Hand; 15 | import net.minecraft.util.SoundCategory; 16 | import net.minecraft.util.text.ITextComponent; 17 | import net.minecraftforge.fml.network.IContainerFactory; 18 | import net.minecraftforge.items.CapabilityItemHandler; 19 | import net.minecraftforge.items.IItemHandler; 20 | import net.minecraftforge.items.SlotItemHandler; 21 | 22 | public class KeyRingContainer extends Container 23 | { 24 | public static class KeyRingSlot extends SlotItemHandler 25 | { 26 | public final PlayerEntity player; 27 | 28 | public KeyRingSlot(PlayerEntity player, IItemHandler inv, int index, int x, int y) 29 | { 30 | super(inv, index, x, y); 31 | this.player = player; 32 | } 33 | 34 | // TODO PITCH 35 | @Override 36 | public void set(ItemStack stack) 37 | { 38 | super.set(stack); 39 | if(!this.player.level.isClientSide) 40 | this.player.level.playSound(null, this.player.getX(), this.player.getY(), this.player.getZ(), LocksSoundEvents.KEY_RING.get(), SoundCategory.PLAYERS, 1f, 1f); 41 | } 42 | 43 | @Override 44 | public ItemStack onTake(PlayerEntity player, ItemStack stack) 45 | { 46 | if(!this.player.level.isClientSide) 47 | this.player.level.playSound(null, this.player.getX(), this.player.getY(), this.player.getZ(), LocksSoundEvents.KEY_RING.get(), SoundCategory.PLAYERS, 1f, 1f); 48 | return super.onTake(player, stack); 49 | } 50 | } 51 | 52 | public final ItemStack stack; 53 | public final IItemHandler inv; 54 | public final int rows; 55 | 56 | public KeyRingContainer(int id, PlayerEntity player, ItemStack stack) 57 | { 58 | super(LocksContainerTypes.KEY_RING.get(), id); 59 | this.stack = stack; 60 | this.inv = stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null); 61 | 62 | this.rows = inv.getSlots() / 9; 63 | for(int row = 0; row < rows; ++row) 64 | for(int col = 0; col < 9; ++col) 65 | this.addSlot(new KeyRingSlot(player, inv, col + row * 9, 8 + col * 18, 18 + row * 18)); 66 | 67 | int offset = (rows - 4) * 18; 68 | for(int row = 0; row < 3; ++row) 69 | for (int col = 0; col < 9; ++col) 70 | this.addSlot(new Slot(player.inventory, col + row * 9 + 9, 8 + col * 18, 103 + row * 18 + offset)); 71 | 72 | for(int coll = 0; coll < 9; ++coll) 73 | this.addSlot(new Slot(player.inventory, coll, 8 + coll * 18, 161 + offset)); 74 | } 75 | 76 | @Override 77 | public boolean stillValid(PlayerEntity player) 78 | { 79 | return !this.stack.isEmpty(); 80 | } 81 | 82 | @Override 83 | public ItemStack quickMoveStack(PlayerEntity player, int index) 84 | { 85 | ItemStack stack = ItemStack.EMPTY; 86 | Slot slot = this.slots.get(index); 87 | if(slot == null || !slot.hasItem()) 88 | return stack; 89 | ItemStack stack1 = slot.getItem(); 90 | stack = stack1.copy(); 91 | if(index < this.inv.getSlots()) 92 | { 93 | if(!this.moveItemStackTo(stack1, this.inv.getSlots(), this.slots.size(), true)) 94 | return ItemStack.EMPTY; 95 | } 96 | else if(!this.moveItemStackTo(stack1, 0, this.inv.getSlots(), false)) 97 | return ItemStack.EMPTY; 98 | if(stack1.isEmpty()) 99 | slot.set(ItemStack.EMPTY); 100 | else 101 | slot.setChanged(); 102 | return stack; 103 | } 104 | 105 | public static final IContainerFactory FACTORY = (id, inv, buffer) -> 106 | { 107 | return new KeyRingContainer(id, inv.player, inv.player.getItemInHand(buffer.readEnum(Hand.class))); 108 | }; 109 | 110 | public static class Provider implements INamedContainerProvider 111 | { 112 | public final ItemStack stack; 113 | 114 | public Provider(ItemStack stack) 115 | { 116 | this.stack = stack; 117 | } 118 | 119 | @Override 120 | public Container createMenu(int id, PlayerInventory inv, PlayerEntity player) 121 | { 122 | return new KeyRingContainer(id, player, this.stack); 123 | } 124 | 125 | @Override 126 | public ITextComponent getDisplayName() 127 | { 128 | return this.stack.getHoverName(); 129 | } 130 | } 131 | 132 | public static class Writer implements Consumer 133 | { 134 | public final Hand hand; 135 | 136 | public Writer(Hand hand) 137 | { 138 | this.hand = hand; 139 | } 140 | 141 | @Override 142 | public void accept(PacketBuffer buffer) 143 | { 144 | buffer.writeEnum(this.hand); 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/mixin/TemplateMixin.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.mixin; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | 7 | import javax.annotation.Nullable; 8 | 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | import melonslise.locks.Locks; 16 | import melonslise.locks.common.capability.ILockableHandler; 17 | import melonslise.locks.common.config.LocksConfig; 18 | import melonslise.locks.common.init.LocksCapabilities; 19 | import melonslise.locks.common.util.Cuboid6i; 20 | import melonslise.locks.common.util.Lock; 21 | import melonslise.locks.common.util.Lockable; 22 | import melonslise.locks.common.util.LockableInfo; 23 | import melonslise.locks.common.util.LocksUtil; 24 | import melonslise.locks.common.util.Transform; 25 | import net.minecraft.block.Block; 26 | import net.minecraft.item.ItemStack; 27 | import net.minecraft.nbt.CompoundNBT; 28 | import net.minecraft.nbt.ListNBT; 29 | import net.minecraft.util.Direction; 30 | import net.minecraft.util.math.BlockPos; 31 | import net.minecraft.world.IServerWorld; 32 | import net.minecraft.world.World; 33 | import net.minecraft.world.gen.feature.template.PlacementSettings; 34 | import net.minecraft.world.gen.feature.template.Template; 35 | import net.minecraftforge.common.util.Constants; 36 | 37 | @Mixin(Template.class) 38 | public class TemplateMixin 39 | { 40 | private final List lockableInfos = new ArrayList<>(); 41 | 42 | @Inject(at = @At("HEAD"), method = "fillFromWorld(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/block/Block;)V") 43 | private void fillFromWorld(World world, BlockPos start, BlockPos size, boolean takeEntities, @Nullable Block toIgnore, CallbackInfo ci) 44 | { 45 | if (size.getX() >= 1 && size.getY() >= 1 && size.getZ() >= 1) 46 | { 47 | this.lockableInfos.clear(); 48 | ILockableHandler handler = world.getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null); 49 | Cuboid6i bb = new Cuboid6i(start, start.offset(size.getX() - 1, size.getY() - 1, size.getZ() - 1)); 50 | handler.getLoaded().values().stream() 51 | .filter(lkb -> lkb.bb.intersects(bb)) 52 | .forEach(lkb -> 53 | { 54 | Cuboid6i newBB = bb.intersection(lkb.bb).offset(-start.getX(), -start.getY(), -start.getZ()); 55 | this.lockableInfos.add(new LockableInfo(newBB, lkb.lock, lkb.tr, lkb.stack, lkb.id)); 56 | }); 57 | } 58 | } 59 | 60 | // Second return 61 | @Inject(at = @At(value = "RETURN", ordinal = 1), method = "placeInWorld(Lnet/minecraft/world/IServerWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/gen/feature/template/PlacementSettings;Ljava/util/Random;I)Z") 62 | private void placeInWorld(IServerWorld world, BlockPos start, BlockPos size, PlacementSettings settings, Random rng, int i, CallbackInfoReturnable cir) 63 | { 64 | World level; 65 | try 66 | { 67 | level = world.getLevel(); 68 | } 69 | catch(Exception e) 70 | { 71 | Locks.LOGGER.warn(world + "#getLevel threw an error! Skipping lockable placement for this template "); 72 | return; 73 | } 74 | ILockableHandler handler = level.getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null); 75 | for(LockableInfo lkb : this.lockableInfos) 76 | { 77 | BlockPos pos1 = LocksUtil.transform(lkb.bb.x1, lkb.bb.y1, lkb.bb.z1, settings); 78 | BlockPos pos2 = LocksUtil.transform(lkb.bb.x2, lkb.bb.y2, lkb.bb.z2, settings); 79 | Cuboid6i bb = new Cuboid6i(pos1.getX() + start.getX(), pos1.getY() + start.getY(), pos1.getZ() + start.getZ(), pos2.getX() + start.getX(), pos2.getY() + start.getY(), pos2.getZ() + start.getZ()); 80 | ItemStack stack = LocksConfig.RANDOMIZE_LOADED_LOCKS.get() ? LocksConfig.getRandomLock(rng) : lkb.stack; 81 | Lock lock = LocksConfig.RANDOMIZE_LOADED_LOCKS.get() ? Lock.from(stack) : lkb.lock; 82 | Transform tr = Transform.fromDirectionAndFace(settings.getRotation().rotate(settings.getMirror().getRotation(lkb.tr.dir).rotate(lkb.tr.dir)), lkb.tr.face, Direction.NORTH); 83 | handler.add(new Lockable(bb, lock, tr, stack, level)); 84 | } 85 | } 86 | 87 | private static final String KEY_LOCKABLES = "Lockables"; 88 | 89 | @Inject(at = @At("HEAD"), method = "save(Lnet/minecraft/nbt/CompoundNBT;)Lnet/minecraft/nbt/CompoundNBT;") 90 | private void save(CompoundNBT nbt, CallbackInfoReturnable cir) 91 | { 92 | ListNBT list = new ListNBT(); 93 | for(LockableInfo lkb : this.lockableInfos) 94 | list.add(LockableInfo.toNbt(lkb)); 95 | nbt.put(KEY_LOCKABLES, list); 96 | } 97 | 98 | @Inject(at = @At("HEAD"), method = "load(Lnet/minecraft/nbt/CompoundNBT;)V") 99 | private void read(CompoundNBT nbt, CallbackInfo ci) 100 | { 101 | this.lockableInfos.clear(); 102 | ListNBT list = nbt.getList(KEY_LOCKABLES, Constants.NBT.TAG_COMPOUND); 103 | for(int a = 0, b = list.size(); a < b; ++a) 104 | this.lockableInfos.add(LockableInfo.fromNbt(list.getCompound(a))); 105 | } 106 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/LocksUtil.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.Deque; 10 | import java.util.Random; 11 | import java.util.stream.Stream; 12 | 13 | import com.google.gson.JsonElement; 14 | import com.google.gson.JsonSyntaxException; 15 | 16 | import melonslise.locks.common.init.LocksCapabilities; 17 | import melonslise.locks.mixin.ForgeHooksAccessor; 18 | import melonslise.locks.mixin.LootPoolAccessor; 19 | import melonslise.locks.mixin.LootTableAccessor; 20 | import net.minecraft.loot.LootPool; 21 | import net.minecraft.loot.LootTable; 22 | import net.minecraft.loot.LootTableManager; 23 | import net.minecraft.resources.IResourceManager; 24 | import net.minecraft.state.properties.AttachFace; 25 | import net.minecraft.util.Direction; 26 | import net.minecraft.util.JSONUtils; 27 | import net.minecraft.util.ResourceLocation; 28 | import net.minecraft.util.math.AxisAlignedBB; 29 | import net.minecraft.util.math.BlockPos; 30 | import net.minecraft.util.math.vector.Vector3d; 31 | import net.minecraft.util.math.vector.Vector3i; 32 | import net.minecraft.world.World; 33 | import net.minecraft.world.gen.feature.template.PlacementSettings; 34 | 35 | public final class LocksUtil 36 | { 37 | public static IResourceManager resourceManager; 38 | 39 | public static Constructor lootTableContextConstructor; 40 | 41 | static 42 | { 43 | try 44 | { 45 | lootTableContextConstructor = Class.forName("net.minecraftforge.common.ForgeHooks$LootTableContext").getDeclaredConstructor(ResourceLocation.class, boolean.class); 46 | lootTableContextConstructor.setAccessible(true); 47 | } 48 | catch (SecurityException | IllegalArgumentException | NoSuchMethodException | ClassNotFoundException e) 49 | { 50 | e.printStackTrace(); 51 | } 52 | } 53 | 54 | private LocksUtil() {} 55 | 56 | public static void shuffle(byte[] array, Random rng) 57 | { 58 | for (int a = array.length - 1; a > 0; --a) 59 | { 60 | int index = rng.nextInt(a + 1); 61 | byte temp = array[index]; 62 | array[index] = array[a]; 63 | array[a] = temp; 64 | } 65 | } 66 | 67 | public static boolean chance(Random rng, double ch) 68 | { 69 | return ch == 1d || ch != 0d && rng.nextDouble() <= ch; 70 | } 71 | 72 | public static BlockPos transform(int x, int y, int z, PlacementSettings settings) 73 | { 74 | switch(settings.getMirror()) 75 | { 76 | case LEFT_RIGHT: 77 | z = -z + 1; 78 | break; 79 | case FRONT_BACK: 80 | x = -x + 1; 81 | break; 82 | default: 83 | break; 84 | } 85 | int x1 = settings.getRotationPivot().getX(); 86 | int z1 = settings.getRotationPivot().getZ(); 87 | switch(settings.getRotation()) 88 | { 89 | case COUNTERCLOCKWISE_90: 90 | return new BlockPos(x1 - z1 + z, y, x1 + z1 - x + 1); 91 | case CLOCKWISE_90: 92 | return new BlockPos(x1 + z1 - z + 1, y, z1 - x1 + x); 93 | case CLOCKWISE_180: 94 | return new BlockPos(x1 + x1 - x + 1, y, z1 + z1 - z + 1); 95 | default: 96 | return new BlockPos(x, y, z); 97 | } 98 | } 99 | 100 | public static AttachFace faceFromDir(Direction dir) 101 | { 102 | return dir == Direction.UP ? AttachFace.CEILING : dir == Direction.DOWN ? AttachFace.FLOOR : AttachFace.WALL; 103 | } 104 | 105 | public static AxisAlignedBB rotateY(AxisAlignedBB bb) 106 | { 107 | return new AxisAlignedBB(bb.minZ, bb.minY, bb.minX, bb.maxZ, bb.maxY, bb.maxX); 108 | } 109 | 110 | public static AxisAlignedBB rotateX(AxisAlignedBB bb) 111 | { 112 | return new AxisAlignedBB(bb.minX, bb.minZ, bb.minY, bb.maxX, bb.maxZ, bb.maxY); 113 | } 114 | 115 | public static boolean intersectsInclusive(AxisAlignedBB bb1, AxisAlignedBB bb2) 116 | { 117 | return bb1.minX <= bb2.maxX && bb1.maxX >= bb2.minX && bb1.minY <= bb2.maxY && bb1.maxY >= bb2.minY && bb1.minZ <= bb2.maxZ && bb1.maxZ >= bb2.minZ; 118 | } 119 | 120 | public static Vector3d sideCenter(AxisAlignedBB bb, Direction side) 121 | { 122 | Vector3i dir = side.getNormal(); 123 | return new Vector3d((bb.minX + bb.maxX + (bb.maxX - bb.minX) * dir.getX()) * 0.5d, (bb.minY + bb.maxY + (bb.maxY - bb.minY) * dir.getY()) * 0.5d, (bb.minZ + bb.maxZ + (bb.maxZ - bb.minZ) * dir.getZ()) * 0.5d); 124 | } 125 | 126 | public static LootTable lootTableFrom(ResourceLocation loc) throws IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 127 | { 128 | JsonElement json = JSONUtils.fromJson(LootTableManager.GSON, new BufferedReader(new InputStreamReader(resourceManager.getResource(loc).getInputStream(), StandardCharsets.UTF_8)), JsonElement.class); 129 | Deque que = ForgeHooksAccessor.getLootContext().get(); 130 | Object lootCtx = lootTableContextConstructor.newInstance(loc, false); 131 | try 132 | { 133 | que.push(lootCtx); 134 | return LootTableManager.GSON.fromJson(json, LootTable.class); 135 | } 136 | catch(JsonSyntaxException e) 137 | { 138 | throw e; 139 | } 140 | finally // Still executes even if catch throws according to SO! 141 | { 142 | que.pop(); 143 | } 144 | } 145 | 146 | // Only merges entries, not conditions and functions 147 | public static LootTable mergeEntries(LootTable table, LootTable inject) 148 | { 149 | for(LootPool injectPool : ((LootTableAccessor) inject).getPools()) 150 | { 151 | LootPool pool = table.getPool(injectPool.getName()); 152 | if(pool == null) 153 | table.addPool(injectPool); 154 | else 155 | ((LootPoolAccessor) pool).getEntries().addAll(((LootPoolAccessor) injectPool).getEntries()); 156 | } 157 | return table; 158 | } 159 | 160 | public static Stream intersecting(World world, BlockPos pos) 161 | { 162 | return world.getCapability(LocksCapabilities.LOCKABLE_HANDLER).lazyMap(cap -> cap.getInChunk(pos).values().stream().filter(lkb -> lkb.bb.intersects(pos))).orElse(Stream.empty()); 163 | } 164 | 165 | public static boolean locked(World world, BlockPos pos) 166 | { 167 | return intersecting(world, pos).anyMatch(LocksPredicates.LOCKED); 168 | } 169 | } -------------------------------------------------------------------------------- /src/main/java/melonslise/locks/common/util/Lockable.java: -------------------------------------------------------------------------------- 1 | package melonslise.locks.common.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Observable; 9 | import java.util.Observer; 10 | 11 | import melonslise.locks.common.init.LocksCapabilities; 12 | import melonslise.locks.common.item.LockItem; 13 | import net.minecraft.block.BlockState; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.renderer.culling.ClippingHelper; 16 | import net.minecraft.item.ItemStack; 17 | import net.minecraft.nbt.CompoundNBT; 18 | import net.minecraft.network.PacketBuffer; 19 | import net.minecraft.state.properties.AttachFace; 20 | import net.minecraft.util.Direction; 21 | import net.minecraft.util.math.AxisAlignedBB; 22 | import net.minecraft.util.math.BlockPos; 23 | import net.minecraft.util.math.shapes.VoxelShape; 24 | import net.minecraft.util.math.vector.Vector3d; 25 | import net.minecraft.world.World; 26 | import net.minecraftforge.api.distmarker.Dist; 27 | import net.minecraftforge.api.distmarker.OnlyIn; 28 | 29 | public class Lockable extends Observable implements Observer 30 | { 31 | public static class State 32 | { 33 | public static final AxisAlignedBB 34 | VERT_Z_BB = new AxisAlignedBB(-2d / 16d, -3d / 16d, 0.5d / 16d, 2d / 16d, 3d / 16d, 0.5d / 16d), 35 | VERT_X_BB = LocksUtil.rotateY(VERT_Z_BB), 36 | HOR_Z_BB = LocksUtil.rotateX(VERT_Z_BB), 37 | HOR_X_BB = LocksUtil.rotateY(HOR_Z_BB); 38 | 39 | public static AxisAlignedBB getBounds(Transform tr) 40 | { 41 | return tr.face == AttachFace.WALL ? tr.dir.getAxis() == Direction.Axis.Z ? VERT_Z_BB : VERT_X_BB : tr.dir.getAxis() == Direction.Axis.Z ? HOR_Z_BB : HOR_X_BB; 42 | } 43 | 44 | public final Vector3d pos; 45 | public final Transform tr; 46 | public final AxisAlignedBB bb; 47 | 48 | public State(Vector3d pos, Transform tr) 49 | { 50 | this(pos, tr, getBounds(tr).move(pos)); 51 | } 52 | 53 | public State(Vector3d pos, Transform tr, AxisAlignedBB bb) 54 | { 55 | this.pos = pos; 56 | this.tr = tr; 57 | this.bb = bb; 58 | } 59 | 60 | @OnlyIn(Dist.CLIENT) 61 | public boolean inView(ClippingHelper ch) 62 | { 63 | return ch.cubeInFrustum(this.bb.minX, this.bb.minY, this.bb.minZ, this.bb.maxX, this.bb.maxY, this.bb.maxZ); 64 | } 65 | 66 | @OnlyIn(Dist.CLIENT) 67 | public boolean inRange(Vector3d pos) 68 | { 69 | Minecraft mc = Minecraft.getInstance(); 70 | double dist = this.pos.distanceToSqr(pos); 71 | double max = mc.options.renderDistance * 8; 72 | return dist < max * max; 73 | } 74 | } 75 | 76 | public final Cuboid6i bb; 77 | public final Lock lock; 78 | public final Transform tr; 79 | public final ItemStack stack; 80 | public final int id; 81 | 82 | public int oldSwingTicks, swingTicks, maxSwingTicks; 83 | 84 | public Map, State> cache = new HashMap<>(6); 85 | 86 | public Lockable(Cuboid6i bb, Lock lock, Transform tr, ItemStack stack, World world) 87 | { 88 | this(bb, lock, tr, stack, world.getCapability(LocksCapabilities.LOCKABLE_HANDLER).orElse(null).nextId()); 89 | } 90 | 91 | public Lockable(Cuboid6i bb, Lock lock, Transform tr, ItemStack stack, int id) 92 | { 93 | this.bb = bb; 94 | this.lock = lock; 95 | this.tr = tr; 96 | this.stack = stack; 97 | this.id = id; 98 | lock.addObserver(this); 99 | } 100 | 101 | public static final String KEY_BB = "Bb", KEY_LOCK = "Lock", KEY_TRANSFORM = "Transform", KEY_STACK = "Stack", KEY_ID = "Id"; 102 | 103 | public static Lockable fromNbt(CompoundNBT nbt) 104 | { 105 | return new Lockable(Cuboid6i.fromNbt(nbt.getCompound(KEY_BB)), Lock.fromNbt(nbt.getCompound(KEY_LOCK)), Transform.values()[(int) nbt.getByte(KEY_TRANSFORM)], ItemStack.of(nbt.getCompound(KEY_STACK)), nbt.getInt(KEY_ID)); 106 | } 107 | 108 | public static CompoundNBT toNbt(Lockable lkb) 109 | { 110 | CompoundNBT nbt = new CompoundNBT(); 111 | nbt.put(KEY_BB, Cuboid6i.toNbt(lkb.bb)); 112 | nbt.put(KEY_LOCK, Lock.toNbt(lkb.lock)); 113 | nbt.putByte(KEY_TRANSFORM, (byte) lkb.tr.ordinal()); 114 | nbt.put(KEY_STACK, lkb.stack.serializeNBT()); 115 | nbt.putInt(KEY_ID, lkb.id); 116 | return nbt; 117 | } 118 | 119 | public static int idFromNbt(CompoundNBT nbt) 120 | { 121 | return nbt.getInt(KEY_ID); 122 | } 123 | 124 | public static Lockable fromBuf(PacketBuffer buf) 125 | { 126 | return new Lockable(Cuboid6i.fromBuf(buf), Lock.fromBuf(buf), buf.readEnum(Transform.class), buf.readItem(), buf.readInt()); 127 | } 128 | 129 | public static void toBuf(PacketBuffer buf, Lockable lkb) 130 | { 131 | Cuboid6i.toBuf(buf, lkb.bb); 132 | Lock.toBuf(buf, lkb.lock); 133 | buf.writeEnum(lkb.tr); 134 | buf.writeItem(lkb.stack); 135 | buf.writeInt(lkb.id); 136 | } 137 | 138 | @Override 139 | public void update(Observable lock, Object data) 140 | { 141 | this.setChanged(); 142 | this.notifyObservers(); 143 | LockItem.setOpen(this.stack, !this.lock.locked); 144 | } 145 | 146 | public void tick() 147 | { 148 | this.oldSwingTicks = this.swingTicks; 149 | if(this.swingTicks > 0) 150 | --this.swingTicks; 151 | } 152 | 153 | public void swing(int ticks) 154 | { 155 | this.swingTicks = this.oldSwingTicks = this.maxSwingTicks = ticks; 156 | } 157 | 158 | // FIXME use array instead of list 159 | public State getLockState(World world) 160 | { 161 | List states = new ArrayList<>(this.bb.volume()); 162 | for(BlockPos pos : this.bb.getContainedPos()) 163 | { 164 | if(!world.hasChunkAt(pos)) 165 | return null; 166 | states.add(world.getBlockState(pos)); 167 | } 168 | State state = this.cache.get(states); 169 | if(state != null) 170 | return state; 171 | ArrayList boxes = new ArrayList<>(4); 172 | for(BlockPos pos : this.bb.getContainedPos()) 173 | { 174 | VoxelShape shape = world.getBlockState(pos).getShape(world, pos); 175 | if(shape.isEmpty()) 176 | continue; 177 | AxisAlignedBB bb = shape.bounds(); 178 | bb = bb.move(pos); 179 | AxisAlignedBB union = bb; 180 | Iterator it = boxes.iterator(); 181 | while(it.hasNext()) 182 | { 183 | AxisAlignedBB bb1 = it.next(); 184 | if(LocksUtil.intersectsInclusive(union, bb1)) 185 | { 186 | union = union.minmax(bb1); 187 | it.remove(); 188 | } 189 | } 190 | boxes.add(union); 191 | } 192 | if(boxes.isEmpty()) 193 | return null; 194 | Direction side = this.tr.getCuboidFace(); 195 | Vector3d center = this.bb.sideCenter(side); 196 | Vector3d point = center; 197 | double min = -1d; 198 | for(AxisAlignedBB box : boxes) 199 | for(Direction side1 : Direction.values()) 200 | { 201 | Vector3d point1 = LocksUtil.sideCenter(box, side1).add(Vector3d.atLowerCornerOf(side1.getNormal()).scale(0.05d)); 202 | double dist = center.distanceToSqr(point1); 203 | if(min != -1d && dist >= min) 204 | continue; 205 | point = point1; 206 | min = dist; 207 | side = side1; 208 | } 209 | state = new State(point, Transform.fromDirection(side, this.tr.dir)); 210 | this.cache.put(states, state); 211 | return state; 212 | } 213 | } --------------------------------------------------------------------------------