├── src └── main │ ├── resources │ ├── assets │ │ └── endermail │ │ │ ├── models │ │ │ ├── item │ │ │ │ ├── locker.json │ │ │ │ ├── package.json │ │ │ │ ├── locker_filled.json │ │ │ │ ├── stamped_package.json │ │ │ │ ├── stamp.json │ │ │ │ ├── packing_tape.json │ │ │ │ ├── package_controller_failure.json │ │ │ │ ├── package_controller_success.json │ │ │ │ └── package_controller.json │ │ │ └── block │ │ │ │ ├── locker.json │ │ │ │ ├── locker_filled.json │ │ │ │ ├── package.json │ │ │ │ └── stamped_package.json │ │ │ ├── textures │ │ │ ├── gui │ │ │ │ ├── locker.png │ │ │ │ ├── stamp.png │ │ │ │ └── package.png │ │ │ ├── item │ │ │ │ ├── stamp.png │ │ │ │ ├── packing_tape.png │ │ │ │ ├── package_controller.png │ │ │ │ ├── package_controller_failure.png │ │ │ │ └── package_controller_success.png │ │ │ ├── block │ │ │ │ ├── locker_front.png │ │ │ │ ├── locker_side.png │ │ │ │ ├── package_side_1.png │ │ │ │ ├── package_side_2.png │ │ │ │ ├── package_side_3.png │ │ │ │ ├── locker_front_filled.png │ │ │ │ └── stamped_package_side_3.png │ │ │ └── models │ │ │ │ └── ender_mailman.png │ │ │ ├── blockstates │ │ │ ├── locker.json │ │ │ └── package.json │ │ │ └── lang │ │ │ ├── zh_cn.json │ │ │ ├── ko_kr.json │ │ │ ├── ja_jp.json │ │ │ ├── en_us.json │ │ │ ├── fr_fr.json │ │ │ ├── pl_pl.json │ │ │ ├── ru_ru.json │ │ │ ├── uk_ua.json │ │ │ ├── pt_br.json │ │ │ ├── de_de.json │ │ │ └── it_it.json │ ├── pack.mcmeta │ ├── data │ │ └── endermail │ │ │ ├── recipes │ │ │ ├── package.json │ │ │ ├── stamp.json │ │ │ ├── packing_tape.json │ │ │ ├── locker.json │ │ │ └── package_controller.json │ │ │ └── loot_tables │ │ │ └── blocks │ │ │ └── locker.json │ └── META-INF │ │ └── mods.toml │ └── java │ └── com │ └── chaosthedude │ └── endermail │ ├── util │ ├── OverlaySide.java │ ├── ControllerState.java │ ├── ItemUtils.java │ └── RenderUtils.java │ ├── gui │ ├── ScreenWrapper.java │ ├── PackageScreen.java │ ├── container │ │ ├── PackageMenu.java │ │ └── LockerMenu.java │ ├── LockerScreen.java │ ├── StampScreen.java │ └── StampTextField.java │ ├── registry │ ├── EnderMailAttributes.java │ ├── EnderMailBlocks.java │ ├── EnderMailRenderers.java │ ├── EnderMailEntities.java │ ├── EnderMailContainers.java │ ├── EnderMailItems.java │ └── EnderMailBlockEntities.java │ ├── client │ ├── render │ │ ├── EnderMailmanRenderer.java │ │ ├── HeldPackageLayer.java │ │ └── model │ │ │ └── EnderMailmanModel.java │ └── ClientEventHandler.java │ ├── network │ ├── ConfigureLockerPacket.java │ └── StampPackagePacket.java │ ├── data │ └── LockerData.java │ ├── config │ └── ConfigHandler.java │ ├── item │ └── PackageControllerItem.java │ ├── EnderMail.java │ ├── block │ ├── LockerBlock.java │ ├── entity │ │ ├── PackageBlockEntity.java │ │ └── LockerBlockEntity.java │ └── PackageBlock.java │ └── entity │ └── EnderMailmanEntity.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitattributes ├── settings.gradle ├── .gitignore ├── README.md ├── gradlew.bat ├── gradle.properties ├── changelog.txt ├── gradlew └── LICENSE.md /src/main/resources/assets/endermail/models/item/locker.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "endermail:block/locker" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "endermail:block/package" 3 | } 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/locker_filled.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "endermail:block/locker_filled" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/stamped_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "endermail:block/stamped_package" 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Ender Mail resources", 4 | "pack_format": 15 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/stamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/handheld", 3 | "textures": { 4 | "layer0": "endermail:item/stamp" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/gui/locker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/gui/locker.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/gui/stamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/gui/stamp.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/item/stamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/item/stamp.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/packing_tape.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/handheld", 3 | "textures": { 4 | "layer0": "endermail:item/packing_tape" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/gui/package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/gui/package.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/locker_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/locker_front.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/locker_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/locker_side.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/item/packing_tape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/item/packing_tape.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/package_side_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/package_side_1.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/package_side_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/package_side_2.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/package_side_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/package_side_3.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/models/ender_mailman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/models/ender_mailman.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/item/package_controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/item/package_controller.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/locker_front_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/locker_front_filled.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/package_controller_failure.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/handheld", 3 | "textures": { 4 | "layer0": "endermail:item/package_controller_failure" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/package_controller_success.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/handheld", 3 | "textures": { 4 | "layer0": "endermail:item/package_controller_success" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/block/stamped_package_side_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/block/stamped_package_side_3.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/item/package_controller_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/item/package_controller_failure.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/textures/item/package_controller_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MattCzyr/EnderMail/HEAD/src/main/resources/assets/endermail/textures/item/package_controller_success.png -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/block/locker.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/orientable", 3 | "textures": { 4 | "top": "endermail:block/locker_side", 5 | "front": "endermail:block/locker_front", 6 | "side": "endermail:block/locker_side" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/block/locker_filled.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/orientable", 3 | "textures": { 4 | "top": "endermail:block/locker_side", 5 | "front": "endermail:block/locker_front_filled", 6 | "side": "endermail:block/locker_side" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Disable autocrlf on generated files, they always generate with LF 2 | # Add any extra files or paths here to make git stop saying they 3 | # are changed when only line endings change. 4 | src/generated/**/.cache/cache text eol=lf 5 | src/generated/**/*.json text eol=lf 6 | -------------------------------------------------------------------------------- /src/main/resources/data/endermail/recipes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "crafting_shapeless", 3 | "ingredients": [ 4 | { 5 | "tag": "forge:chests/wooden" 6 | }, 7 | { 8 | "item": "endermail:packing_tape" 9 | } 10 | ], 11 | "result": { 12 | "item": "endermail:package" 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/util/OverlaySide.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.util; 2 | 3 | public enum OverlaySide { 4 | 5 | LEFT, 6 | RIGHT; 7 | 8 | public static OverlaySide fromString(String str) { 9 | if (str.equals("RIGHT")) { 10 | return RIGHT; 11 | } 12 | return LEFT; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/main/resources/data/endermail/recipes/stamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "crafting_shapeless", 3 | "ingredients": [ 4 | { 5 | "item": "minecraft:paper" 6 | }, 7 | { 8 | "item": "minecraft:ender_pearl" 9 | } 10 | ], 11 | "result": { 12 | "item": "endermail:stamp", 13 | "count": 4 14 | } 15 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { 5 | name = 'MinecraftForge' 6 | url = 'https://maven.minecraftforge.net/' 7 | } 8 | } 9 | } 10 | 11 | plugins { 12 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' 13 | } -------------------------------------------------------------------------------- /src/main/resources/data/endermail/recipes/packing_tape.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "crafting_shapeless", 3 | "ingredients": [ 4 | { 5 | "item": "minecraft:paper" 6 | }, 7 | { 8 | "tag": "forge:slimeballs" 9 | } 10 | ], 11 | "result": { 12 | "item": "endermail:packing_tape", 13 | "count": 4 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/data/endermail/recipes/locker.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "crafting_shaped", 3 | "pattern": [ 4 | "III", 5 | "ISI", 6 | "III" 7 | ], 8 | "key": { 9 | "I": { 10 | "item": "minecraft:iron_ingot" 11 | }, 12 | "S": { 13 | "item": "endermail:stamp" 14 | } 15 | }, 16 | "result": { 17 | "item": "endermail:locker" 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/resources/data/endermail/recipes/package_controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "crafting_shaped", 3 | "pattern": [ 4 | "III", 5 | "IPI", 6 | "III" 7 | ], 8 | "key": { 9 | "I": { 10 | "item": "minecraft:iron_ingot" 11 | }, 12 | "P": { 13 | "item": "endermail:package" 14 | } 15 | }, 16 | "result": { 17 | "item": "endermail:package_controller" 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/block/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/orientable", 3 | "textures": { 4 | "up": "endermail:block/package_side_3", 5 | "down": "endermail:block/package_side_1", 6 | "north": "endermail:block/package_side_1", 7 | "south": "endermail:block/package_side_1", 8 | "east": "endermail:block/package_side_2", 9 | "west": "endermail:block/package_side_2", 10 | "front": "endermail:block/package_side_1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/block/stamped_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/orientable", 3 | "textures": { 4 | "up": "endermail:block/stamped_package_side_3", 5 | "down": "endermail:block/package_side_1", 6 | "north": "endermail:block/package_side_1", 7 | "south": "endermail:block/package_side_1", 8 | "east": "endermail:block/package_side_2", 9 | "west": "endermail:block/package_side_2", 10 | "particle": "endermail:block/package_side_1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/data/endermail/loot_tables/blocks/locker.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "endermail:locker", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "name": "endermail:locker" 10 | } 11 | ], 12 | "conditions": [ 13 | { 14 | "condition": "minecraft:survives_explosion" 15 | } 16 | ] 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/ScreenWrapper.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.entity.player.Player; 6 | import net.minecraft.world.level.Level; 7 | import net.minecraftforge.api.distmarker.Dist; 8 | import net.minecraftforge.api.distmarker.OnlyIn; 9 | 10 | @OnlyIn(Dist.CLIENT) 11 | public class ScreenWrapper { 12 | 13 | public static void openStampScreen(Level level, Player player, BlockPos packagePos) { 14 | Minecraft.getInstance().setScreen(new StampScreen(level, player, packagePos)); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/util/ControllerState.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.util; 2 | 3 | public enum ControllerState { 4 | 5 | DEFAULT(0), 6 | DELIVERING(1), 7 | DELIVERED(2), 8 | DELIVERED_TO_LOCKER(3), 9 | UNDELIVERABLE(4), 10 | TOOFAR(5), 11 | INVALID_LOCKER(6); 12 | 13 | private int id; 14 | 15 | ControllerState(int id) { 16 | this.id = id; 17 | } 18 | 19 | public int getID() { 20 | return id; 21 | } 22 | 23 | public static ControllerState fromID(int id) { 24 | for (ControllerState state : values()) { 25 | if (state.getID() == id) { 26 | return state; 27 | } 28 | } 29 | 30 | return null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailAttributes.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.entity.EnderMailmanEntity; 4 | 5 | import net.minecraftforge.event.entity.EntityAttributeCreationEvent; 6 | import net.minecraftforge.eventbus.api.SubscribeEvent; 7 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 8 | 9 | @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) 10 | public class EnderMailAttributes { 11 | 12 | @SubscribeEvent 13 | public static void registerAttributes(final EntityAttributeCreationEvent event) { 14 | event.put(EnderMailEntities.ENDER_MAILMAN.get(), EnderMailmanEntity.createAttributes().build()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/blockstates/locker.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=north,filled=false": { "model": "endermail:block/locker" }, 4 | "facing=south,filled=false": { "model": "endermail:block/locker", "y": 180 }, 5 | "facing=west,filled=false": { "model": "endermail:block/locker", "y": 270 }, 6 | "facing=east,filled=false": { "model": "endermail:block/locker", "y": 90 }, 7 | "facing=north,filled=true": { "model": "endermail:block/locker_filled" }, 8 | "facing=south,filled=true": { "model": "endermail:block/locker_filled", "y": 180 }, 9 | "facing=west,filled=true": { "model": "endermail:block/locker_filled", "y": 270 }, 10 | "facing=east,filled=true": { "model": "endermail:block/locker_filled", "y": 90 } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/blockstates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=north,stamped=false": { "model": "endermail:block/package" }, 4 | "facing=south,stamped=false": { "model": "endermail:block/package", "y": 180 }, 5 | "facing=west,stamped=false": { "model": "endermail:block/package", "y": 270 }, 6 | "facing=east,stamped=false": { "model": "endermail:block/package", "y": 90 }, 7 | "facing=north,stamped=true": { "model": "endermail:block/stamped_package" }, 8 | "facing=south,stamped=true": { "model": "endermail:block/stamped_package", "y": 180 }, 9 | "facing=west,stamped=true": { "model": "endermail:block/stamped_package", "y": 270 }, 10 | "facing=east,stamped=true": { "model": "endermail:block/stamped_package", "y": 90 } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[47,)" 3 | issueTrackerURL="https://github.com/MattCzyr/EnderMail/issues" 4 | license="CC BY-NC-SA 4.0" 5 | [[mods]] 6 | modId="endermail" 7 | version="1.20.1-1.2.9" 8 | displayName="Ender Mail" 9 | displayURL="https://github.com/MattCzyr/EnderMail" 10 | authors="ChaosTheDude" 11 | description=''' 12 | Employ Endermen to deliver packages anywhere in the world. 13 | ''' 14 | [[dependencies.endermail]] 15 | modId="forge" 16 | mandatory=true 17 | versionRange="[47,)" 18 | ordering="NONE" 19 | side="BOTH" 20 | [[dependencies.endermail]] 21 | modId="minecraft" 22 | mandatory=true 23 | versionRange="[1.20.1]" 24 | ordering="NONE" 25 | side="BOTH" 26 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/models/item/package_controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/handheld", 3 | "textures": { 4 | "layer0": "endermail:item/package_controller" 5 | }, 6 | "overrides": [ 7 | { "predicate": { "endermail:state": 0.0 }, "model": "endermail:item/package_controller" }, 8 | { "predicate": { "endermail:state": 0.1 }, "model": "endermail:item/package_controller" }, 9 | { "predicate": { "endermail:state": 0.2 }, "model": "endermail:item/package_controller_success" }, 10 | { "predicate": { "endermail:state": 0.3 }, "model": "endermail:item/package_controller_success" }, 11 | { "predicate": { "endermail:state": 0.4 }, "model": "endermail:item/package_controller_failure" }, 12 | { "predicate": { "endermail:state": 0.5 }, "model": "endermail:item/package_controller_failure" } 13 | ] 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Gradle 6 | /.gradle 7 | /build 8 | *.launch 9 | 10 | # ForgeGradle 11 | /run 12 | 13 | # Eclipse 14 | /.settings 15 | /.metadata 16 | /.classpath 17 | /.project 18 | /eclipse 19 | /bin 20 | 21 | # Folder config file 22 | Desktop.ini 23 | 24 | # Recycle Bin used on file shares 25 | $RECYCLE.BIN/ 26 | 27 | # Windows Installer files 28 | *.cab 29 | *.msi 30 | *.msm 31 | *.msp 32 | 33 | # Windows shortcuts 34 | *.lnk 35 | 36 | # OSX 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | 52 | # Directories potentially created on remote AFP share 53 | .AppleDB 54 | .AppleDesktop 55 | Network Trash Folder 56 | Temporary Items 57 | .apdisk 58 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailBlocks.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.block.LockerBlock; 5 | import com.chaosthedude.endermail.block.PackageBlock; 6 | import com.google.common.base.Supplier; 7 | 8 | import net.minecraft.world.level.block.Block; 9 | import net.minecraftforge.registries.DeferredRegister; 10 | import net.minecraftforge.registries.ForgeRegistries; 11 | import net.minecraftforge.registries.RegistryObject; 12 | 13 | public class EnderMailBlocks { 14 | 15 | public static final DeferredRegister BLOCK_DEFERRED = DeferredRegister.create(ForgeRegistries.BLOCKS, EnderMail.MODID); 16 | 17 | public static final RegistryObject PACKAGE = register(PackageBlock.NAME, () -> new PackageBlock()); 18 | public static final RegistryObject LOCKER = register(LockerBlock.NAME, () -> new LockerBlock()); 19 | 20 | public static RegistryObject register(String name, Supplier init) { 21 | return BLOCK_DEFERRED.register(name, init); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailRenderers.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.client.render.EnderMailmanRenderer; 4 | import com.chaosthedude.endermail.client.render.model.EnderMailmanModel; 5 | 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.api.distmarker.OnlyIn; 8 | import net.minecraftforge.client.event.EntityRenderersEvent; 9 | import net.minecraftforge.eventbus.api.SubscribeEvent; 10 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 11 | 12 | @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) 13 | @OnlyIn(Dist.CLIENT) 14 | public class EnderMailRenderers { 15 | 16 | @SubscribeEvent 17 | public static void registerRenderers(final EntityRenderersEvent.RegisterRenderers event) { 18 | event.registerEntityRenderer(EnderMailEntities.ENDER_MAILMAN.get(), (context) -> new EnderMailmanRenderer(context)); 19 | } 20 | 21 | @SubscribeEvent 22 | public static void registerLayers(final EntityRenderersEvent.RegisterLayerDefinitions event) { 23 | event.registerLayerDefinition(EnderMailmanModel.LOCATION, () -> EnderMailmanModel.createBodyLayer()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/util/ItemUtils.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.util; 2 | 3 | import com.chaosthedude.endermail.registry.EnderMailItems; 4 | 5 | import net.minecraft.nbt.CompoundTag; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraft.world.item.Item; 8 | import net.minecraft.world.item.ItemStack; 9 | 10 | public class ItemUtils { 11 | 12 | public static boolean verifyNBT(ItemStack stack) { 13 | if (stack.isEmpty() || stack.getItem() != EnderMailItems.PACKAGE_CONTROLLER.get()) { 14 | return false; 15 | } else if (!stack.hasTag()) { 16 | stack.setTag(new CompoundTag()); 17 | } 18 | 19 | return true; 20 | } 21 | 22 | public static ItemStack getHeldItem(Player player, Item item) { 23 | if (!player.getMainHandItem().isEmpty() && player.getMainHandItem().getItem() == item) { 24 | return player.getMainHandItem(); 25 | } else if (!player.getOffhandItem().isEmpty() && player.getOffhandItem().getItem() == item) { 26 | return player.getOffhandItem(); 27 | } 28 | 29 | return ItemStack.EMPTY; 30 | } 31 | 32 | public static boolean isHolding(Player player, Item item) { 33 | return player.getMainHandItem().getItem() == item || player.getOffhandItem().getItem() == item; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ender Mail 2 | 3 | Ender Mail is a Minecraft mod that allows players to deliver packages anywhere in the world. 4 | 5 | ## Download 6 | 7 | Downloads, installation instructions, and more information can be found on [Curse](https://www.curseforge.com/minecraft/mc-mods/ender-mail). 8 | 9 | ## Develop 10 | 11 | ### Setup 12 | 13 | Fork this repository, then clone via SSH: 14 | ``` 15 | git clone git@github.com:/EnderMail.git 16 | ``` 17 | 18 | Or, clone via HTTPS: 19 | ``` 20 | git clone https://github.com//EnderMail.git 21 | ``` 22 | 23 | 2. In the root of the repository, run: 24 | ``` 25 | gradlew eclipse 26 | ``` 27 | 28 | Or, if you plan to use IntelliJ, run: 29 | ``` 30 | gradlew idea 31 | ``` 32 | 33 | 3. Run: 34 | ``` 35 | gradlew genEclipseRuns 36 | ``` 37 | 38 | Or, to use IntelliJ, run: 39 | ``` 40 | gradlew genIntellijRuns 41 | ``` 42 | 43 | 4. Open the project's parent directory in your IDE and import the project as an existing Gradle project. 44 | 45 | ### Build 46 | 47 | To build the project, configure `build.gradle` then run: 48 | ``` 49 | gradlew build 50 | ``` 51 | 52 | This will build a jar file in `build/libs`. 53 | 54 | ## License 55 | 56 | This mod is available under the [Creative Commons Attribution-NonCommercial ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). 57 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailEntities.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.entity.EnderMailmanEntity; 5 | import com.google.common.base.Supplier; 6 | 7 | import net.minecraft.world.entity.Entity; 8 | import net.minecraft.world.entity.EntityType; 9 | import net.minecraft.world.entity.MobCategory; 10 | import net.minecraftforge.registries.DeferredRegister; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | import net.minecraftforge.registries.RegistryObject; 13 | 14 | public class EnderMailEntities { 15 | 16 | public static final DeferredRegister> ENTITY_DEFERRED = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, EnderMail.MODID); 17 | 18 | public static final RegistryObject> ENDER_MAILMAN = register(EnderMailmanEntity.NAME, () -> EntityType.Builder.of(EnderMailmanEntity::new, MobCategory.MONSTER).setTrackingRange(80).setUpdateInterval(3).setShouldReceiveVelocityUpdates(true).sized(0.6F, 2.9F)); 19 | 20 | public static RegistryObject> register(String name, Supplier> init) { 21 | return ENTITY_DEFERRED.register(name, () -> init.get().build(EnderMail.MODID + ":" + name)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Chinese(Simplified) - China Mainland", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "包裹", 7 | "block.endermail.locker": "储物柜", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "包裹控制器", 11 | "item.endermail.packing_tape": "包装胶带", 12 | "item.endermail.stamp": "邮票", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "取消", 16 | "string.endermail.close": "关闭", 17 | "string.endermail.confirm": "确认", 18 | "string.endermail.coordinates": "坐标", 19 | "string.endermail.deliver": "递送者", 20 | "string.endermail.delivered": "已送达", 21 | "string.endermail.deliveredToLocker": "已送达储物柜", 22 | "string.endermail.delivering": "递送中", 23 | "string.endermail.deliveryDistance": "递送距离", 24 | "string.endermail.deliveryLocation": "递送位置", 25 | "string.endermail.error": "发生了一个错误", 26 | "string.endermail.holdShift": "按住 Shift 查看内容", 27 | "string.endermail.lockerID": "储物柜 ID", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "储物柜 ID 无效", 30 | "string.endermail.maxDistance": "最大距离", 31 | "string.endermail.setID": "设置 ID", 32 | "string.endermail.status": "状态", 33 | "string.endermail.tooFar": "距离太远,无法递送", 34 | "string.endermail.undeliverable": "无法递送" 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/ko_kr.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "한글 - 대한민국", 4 | 5 | "_comment": "블록", 6 | "block.endermail.package": "소포", 7 | "block.endermail.locker": "사물함", 8 | 9 | "_comment": "아이템", 10 | "item.endermail.package_controller": "소포 컨트롤러", 11 | "item.endermail.packing_tape": "포장 테이프", 12 | "item.endermail.stamp": "도장", 13 | 14 | "_comment": "문자열", 15 | "string.endermail.cancel": "취소", 16 | "string.endermail.close": "닫기", 17 | "string.endermail.confirm": "확인", 18 | "string.endermail.coordinates": "좌표", 19 | "string.endermail.deliver": "배달", 20 | "string.endermail.delivered": "배달됨", 21 | "string.endermail.deliveredToLocker": "사물함에 배달됨", 22 | "string.endermail.delivering": "배달 중", 23 | "string.endermail.deliveryDistance": "배달 거리", 24 | "string.endermail.deliveryLocation": "배달 위치", 25 | "string.endermail.error": "에러가 발생했습니다.", 26 | "string.endermail.holdShift": "내용물을 얻으려면 쉬프트를 누르세요.", 27 | "string.endermail.lockerID": "사물함 ID", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "유효하지 않은 사물함 ID", 30 | "string.endermail.maxDistance": "최대 거리", 31 | "string.endermail.setID": "ID 설정", 32 | "string.endermail.status": "상태", 33 | "string.endermail.tooFar": "배달하기에는 너무 멉니다.", 34 | "string.endermail.undeliverable": "배달 불가" 35 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/ja_jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Japanese - JAPAN", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "パッケージ", 7 | "block.endermail.locker": "ロッカー", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "貨物コントローラー", 11 | "item.endermail.packing_tape": "パッキングテープ", 12 | "item.endermail.stamp": "スタンプ", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "キャンセル", 16 | "string.endermail.close": "閉じる", 17 | "string.endermail.confirm": "決定", 18 | "string.endermail.coordinates": "座標", 19 | "string.endermail.deliver": "配達", 20 | "string.endermail.delivered": "配達済み", 21 | "string.endermail.deliveredToLocker": "ロッカーに配達済み", 22 | "string.endermail.delivering": "配達中", 23 | "string.endermail.deliveryDistance": "配達距離", 24 | "string.endermail.deliveryLocation": "配達位置", 25 | "string.endermail.error": "エラーが発生した。", 26 | "string.endermail.holdShift": "シフト長押しで内容物を確認", 27 | "string.endermail.lockerID": "ロッカーID", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "無効なロッカーIDです", 30 | "string.endermail.maxDistance": "最大距離", 31 | "string.endermail.setID": "IDを設定", 32 | "string.endermail.status": "ステータス", 33 | "string.endermail.tooFar": "遠すぎる!", 34 | "string.endermail.undeliverable": "配達失敗" 35 | } -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailContainers.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.gui.container.LockerMenu; 5 | import com.chaosthedude.endermail.gui.container.PackageMenu; 6 | import com.google.common.base.Supplier; 7 | 8 | import net.minecraft.world.flag.FeatureFlags; 9 | import net.minecraft.world.inventory.AbstractContainerMenu; 10 | import net.minecraft.world.inventory.MenuType; 11 | import net.minecraftforge.common.extensions.IForgeMenuType; 12 | import net.minecraftforge.registries.DeferredRegister; 13 | import net.minecraftforge.registries.ForgeRegistries; 14 | import net.minecraftforge.registries.RegistryObject; 15 | 16 | public class EnderMailContainers { 17 | 18 | public static final DeferredRegister> CONTAINER_DEFERRED = DeferredRegister.create(ForgeRegistries.MENU_TYPES, EnderMail.MODID); 19 | 20 | public static final RegistryObject> PACKAGE_CONTAINER = register(PackageMenu.NAME, () -> new MenuType(PackageMenu::new, FeatureFlags.DEFAULT_FLAGS)); 21 | public static final RegistryObject> LOCKER_CONTAINER = register(LockerMenu.NAME, () -> IForgeMenuType.create(LockerMenu::new)); 22 | 23 | public static RegistryObject> register(String name, Supplier> init) { 24 | return CONTAINER_DEFERRED.register(name, init); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "English - UNITED STATES", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "Package", 7 | "block.endermail.locker": "Locker", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "Package Controller", 11 | "item.endermail.packing_tape": "Packing Tape", 12 | "item.endermail.stamp": "Stamp", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "Cancel", 16 | "string.endermail.close": "Close", 17 | "string.endermail.confirm": "Confirm", 18 | "string.endermail.coordinates": "Coordinates", 19 | "string.endermail.deliver": "Deliver", 20 | "string.endermail.delivered": "Delivered", 21 | "string.endermail.deliveredToLocker": "Delivered to Locker", 22 | "string.endermail.delivering": "Delivering", 23 | "string.endermail.deliveryDistance": "Delivery Distance", 24 | "string.endermail.deliveryLocation": "Delivery Location", 25 | "string.endermail.error": "An error has occurred", 26 | "string.endermail.holdShift": "Hold Shift for contents", 27 | "string.endermail.lockerID": "Locker ID", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "Invalid Locker ID", 30 | "string.endermail.maxDistance": "Max Distance", 31 | "string.endermail.setID": "Set ID", 32 | "string.endermail.status": "Status", 33 | "string.endermail.tooFar": "Too far to deliver", 34 | "string.endermail.undeliverable": "Undeliverable" 35 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/fr_fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Français - France", 4 | 5 | "_comment": "BLOCS", 6 | "block.endermail.package": "Colis", 7 | "block.endermail.locker": "Casier", 8 | 9 | "_comment": "OBJETS", 10 | "item.endermail.package_controller": "Contrôleur de colis", 11 | "item.endermail.packing_tape": "Ruban adhésif", 12 | "item.endermail.stamp": "Timbre", 13 | 14 | "_comment": "CHAÎNES", 15 | "string.endermail.cancel": "Annuler", 16 | "string.endermail.close": "Fermer", 17 | "string.endermail.confirm": "Confirmer", 18 | "string.endermail.coordinates": "Coordonées", 19 | "string.endermail.deliver": "Livrer", 20 | "string.endermail.delivered": "Livré", 21 | "string.endermail.deliveredToLocker": "Livré dans le casier", 22 | "string.endermail.delivering": "Livraison", 23 | "string.endermail.deliveryDistance": "Distance de livraison", 24 | "string.endermail.deliveryLocation": "Lieu de livraison", 25 | "string.endermail.error": "Une erreur est survenue", 26 | "string.endermail.holdShift": "Maintenez la touche Maj pour voir le contenu", 27 | "string.endermail.lockerID": "ID du casier", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "ID du casier non valide", 30 | "string.endermail.maxDistance": "Distance maximale", 31 | "string.endermail.setID": "Choisisez ID", 32 | "string.endermail.status": "Statut", 33 | "string.endermail.tooFar": "Trop loin pour livrer", 34 | "string.endermail.undeliverable": "Non livrable" 35 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/pl_pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Polski", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "Paczka", 7 | "block.endermail.locker": "Skrzynka pocztowa", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "Kontroler paczek", 11 | "item.endermail.packing_tape": "Taśma pakowa", 12 | "item.endermail.stamp": "Znaczek", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "Anuluj", 16 | "string.endermail.close": "Zamknij", 17 | "string.endermail.confirm": "Potwierdź", 18 | "string.endermail.coordinates": "Koordynaty", 19 | "string.endermail.deliver": "Dostarcz", 20 | "string.endermail.delivered": "Dostarczona", 21 | "string.endermail.deliveredToLocker": "Dostarczona do skrzynki", 22 | "string.endermail.delivering": "W dostawie", 23 | "string.endermail.deliveryDistance": "Zasięg dostawy", 24 | "string.endermail.deliveryLocation": "Miejsce dostawy", 25 | "string.endermail.error": "Wystąpił błąd", 26 | "string.endermail.holdShift": "Przytrzymaj Shift żeby zobaczyć zawartość", 27 | "string.endermail.lockerID": "ID Skrzynki", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "Niewłaściwe ID skrzynki", 30 | "string.endermail.maxDistance": "Maksymalny zasięg", 31 | "string.endermail.setID": "Ustaw ID", 32 | "string.endermail.status": "Status", 33 | "string.endermail.tooFar": "Zbyt daleko aby dostarczyć", 34 | "string.endermail.undeliverable": "Nie da się dostarczyć" 35 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/ru_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Russian - Russian Federation", 4 | 5 | "_comment": "БЛОКИ", 6 | "block.endermail.package": "Посылка", 7 | "block.endermail.locker": "Ячейка", 8 | 9 | "_comment": "ПРЕДМЕТЫ", 10 | "item.endermail.package_controller": "Контроллер посылки", 11 | "item.endermail.packing_tape": "Скотч", 12 | "item.endermail.stamp": "Марка", 13 | 14 | "_comment": "СТРОКИ", 15 | "string.endermail.cancel": "Отмена", 16 | "string.endermail.close": "Закрыть", 17 | "string.endermail.confirm": "Утвердить", 18 | "string.endermail.coordinates": "Координаты", 19 | "string.endermail.deliver": "Доставить", 20 | "string.endermail.delivered": "Доставлено", 21 | "string.endermail.deliveredToLocker": "Отправлено в ячейке", 22 | "string.endermail.delivering": "Доставление", 23 | "string.endermail.deliveryDistance": "Расстояние доставки", 24 | "string.endermail.deliveryLocation": "Место доставки", 25 | "string.endermail.error": "Произошла ошибка", 26 | "string.endermail.holdShift": "Удерживайте Shift для содержимого", 27 | "string.endermail.lockerID": "ID ячейки", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "Неверный идентификатор шкафчика", 30 | "string.endermail.maxDistance": "Макс. расстояние", 31 | "string.endermail.setID": "Установить ID", 32 | "string.endermail.status": "Статус", 33 | "string.endermail.tooFar": "Слишком далеко, чтобы доставить", 34 | "string.endermail.undeliverable": "Невозможно доставить" 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/uk_ua.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Ukrainian - UKRAINE", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "Посилка", 7 | "block.endermail.locker": "Поштомат", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "Поштовий пульт", 11 | "item.endermail.packing_tape": "Пакувальна стрічка", 12 | "item.endermail.stamp": "Марка", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "Скасувати", 16 | "string.endermail.close": "Закрити", 17 | "string.endermail.confirm": "Підтвердити", 18 | "string.endermail.coordinates": "Координати", 19 | "string.endermail.deliver": "Доставити", 20 | "string.endermail.delivered": "Доставлено", 21 | "string.endermail.deliveredToLocker": "Доставлено у поштомат", 22 | "string.endermail.delivering": "У процесі", 23 | "string.endermail.deliveryDistance": "Відстань доставки", 24 | "string.endermail.deliveryLocation": "Адреса доставки", 25 | "string.endermail.error": "Виникла помилка", 26 | "string.endermail.holdShift": "Утримуйте Shift, щоб переглянути вміст", 27 | "string.endermail.lockerID": "ID поштомата", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "Неправильний ID поштомата", 30 | "string.endermail.maxDistance": "Максимальна відстань", 31 | "string.endermail.setID": "Установити ID", 32 | "string.endermail.status": "Стан", 33 | "string.endermail.tooFar": "Занадто далеко для доставки", 34 | "string.endermail.undeliverable": "Неможливо доставити" 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/PackageScreen.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.gui.container.PackageMenu; 5 | 6 | import net.minecraft.client.gui.GuiGraphics; 7 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.resources.ResourceLocation; 10 | import net.minecraft.world.entity.player.Inventory; 11 | import net.minecraftforge.api.distmarker.Dist; 12 | import net.minecraftforge.api.distmarker.OnlyIn; 13 | 14 | @OnlyIn(Dist.CLIENT) 15 | public class PackageScreen extends AbstractContainerScreen { 16 | 17 | private static final ResourceLocation TEXTURE = new ResourceLocation(EnderMail.MODID, "textures/gui/package.png"); 18 | 19 | public PackageScreen(PackageMenu containerPackage, Inventory playerInventory, Component title) { 20 | super(containerPackage, playerInventory, title); 21 | imageHeight = 133; 22 | inventoryLabelY = imageHeight - 94; 23 | } 24 | 25 | @Override 26 | public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { 27 | renderBackground(guiGraphics); 28 | super.render(guiGraphics, mouseX, mouseY, partialTicks); 29 | renderTooltip(guiGraphics, mouseX, mouseY); 30 | } 31 | 32 | @Override 33 | protected void renderBg(GuiGraphics guiGraphics, float partialTicks, int mouseX, int mouseY) { 34 | int i = (width - imageWidth) / 2; 35 | int j = (height - imageHeight) / 2; 36 | guiGraphics.blit(TEXTURE, i, j, 0, 0, imageWidth, imageHeight); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/pt_br.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Portuguese - BRAZIL", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "Pacote", 7 | "block.endermail.locker": "Armário", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "Controlador de pacote", 11 | "item.endermail.packing_tape": "Fita de embalagem", 12 | "item.endermail.stamp": "Carimbo", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "Cancelar", 16 | "string.endermail.close": "Perto", 17 | "string.endermail.confirm": "Confirmar", 18 | "string.endermail.coordinates": "Coordenadas", 19 | "string.endermail.deliver": "Entregar", 20 | "string.endermail.delivered": "Entregue", 21 | "string.endermail.deliveredToLocker": "Entregue para o armário", 22 | "string.endermail.delivering": "Entregando", 23 | "string.endermail.deliveryDistance": "Distância de entrega", 24 | "string.endermail.deliveryLocation": "Local de entrega", 25 | "string.endermail.error": "ocorreu um erro", 26 | "string.endermail.holdShift": "Segure Shift para o conteúdo", 27 | "string.endermail.lockerID": "Código do armário", 28 | "string.endermail.id": "ID", 29 | "string.endermail.invalidLockerID": "ID do armário inválido", 30 | "string.endermail.maxDistance": "Distância máxima", 31 | "string.endermail.setID": "Definir ID", 32 | "string.endermail.status": "Status", 33 | "string.endermail.tooFar": "Muito longe para entregar", 34 | "string.endermail.undeliverable": "Não-entregável" 35 | } -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/de_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "German - Germany", 4 | 5 | "_comment": "BLOCKS", 6 | "block.endermail.package": "Paket", 7 | "block.endermail.locker": "Postfach", 8 | 9 | "_comment": "ITEMS", 10 | "item.endermail.package_controller": "Paketsteuerung", 11 | "item.endermail.packing_tape": "Packband", 12 | "item.endermail.stamp": "Briefmarke", 13 | 14 | "_comment": "STRINGS", 15 | "string.endermail.cancel": "Abbrechen", 16 | "string.endermail.close": "Schließen", 17 | "string.endermail.confirm": "Bestätigen", 18 | "string.endermail.coordinates": "Koordinaten", 19 | "string.endermail.deliver": "Zustellen", 20 | "string.endermail.delivered": "Zugestellt", 21 | "string.endermail.deliveredToLocker": "Zugestellt zum Postfach", 22 | "string.endermail.delivering": "In Zustellung", 23 | "string.endermail.deliveryDistance": "Lieferabstand", 24 | "string.endermail.deliveryLocation": "Lieferungort", 25 | "string.endermail.error": "Ein Fehler ist aufgetreten.", 26 | "string.endermail.holdShift": "Halte Shift, um den Inhalt zu sehen", 27 | "string.endermail.lockerID": "Postfachadresse", 28 | "string.endermail.id": "Adresse", 29 | "string.endermail.invalidLockerID": "Ungültige Postfachadresse", 30 | "string.endermail.maxDistance": "Maximaler Abstand", 31 | "string.endermail.setID": "Postfachadresse festlegen", 32 | "string.endermail.status": "Status", 33 | "string.endermail.tooFar": "Zu weit weg für eine Zustellung", 34 | "string.endermail.undeliverable": "Nicht zustellbar" 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/client/render/EnderMailmanRenderer.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.client.render; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.client.render.model.EnderMailmanModel; 5 | import com.chaosthedude.endermail.entity.EnderMailmanEntity; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | 8 | import net.minecraft.client.renderer.MultiBufferSource; 9 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 10 | import net.minecraft.client.renderer.entity.MobRenderer; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraftforge.api.distmarker.Dist; 13 | import net.minecraftforge.api.distmarker.OnlyIn; 14 | 15 | @OnlyIn(Dist.CLIENT) 16 | public class EnderMailmanRenderer extends MobRenderer { 17 | 18 | private static final ResourceLocation TEXTURE = new ResourceLocation(EnderMail.MODID, "textures/models/ender_mailman.png"); 19 | 20 | public EnderMailmanRenderer(EntityRendererProvider.Context context) { 21 | super(context, new EnderMailmanModel(context.bakeLayer(EnderMailmanModel.LOCATION)), 0.5F); 22 | addLayer(new HeldPackageLayer(this)); 23 | } 24 | 25 | @Override 26 | public void render(EnderMailmanEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight) { 27 | EnderMailmanModel model = getModel(); 28 | model.carrying = entity.isCarryingPackage(); 29 | super.render(entity, entityYaw, partialTicks, poseStack, buffer, packedLight); 30 | } 31 | 32 | @Override 33 | public ResourceLocation getTextureLocation(EnderMailmanEntity entity) { 34 | return TEXTURE; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailItems.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.block.LockerBlock; 5 | import com.chaosthedude.endermail.block.PackageBlock; 6 | import com.chaosthedude.endermail.item.PackageControllerItem; 7 | import com.google.common.base.Supplier; 8 | 9 | import net.minecraft.world.item.BlockItem; 10 | import net.minecraft.world.item.Item; 11 | import net.minecraft.world.item.Item.Properties; 12 | import net.minecraftforge.registries.DeferredRegister; 13 | import net.minecraftforge.registries.ForgeRegistries; 14 | import net.minecraftforge.registries.RegistryObject; 15 | 16 | public class EnderMailItems { 17 | 18 | public static final DeferredRegister ITEM_DEFERRED = DeferredRegister.create(ForgeRegistries.ITEMS, EnderMail.MODID); 19 | 20 | public static final RegistryObject PACKAGE = register(PackageBlock.NAME, () -> new BlockItem(EnderMailBlocks.PACKAGE.get(), new Properties())); 21 | public static final RegistryObject LOCKER = register(LockerBlock.NAME, () -> new BlockItem(EnderMailBlocks.LOCKER.get(), new Properties())); 22 | public static final RegistryObject PACKAGE_CONTROLLER = register(PackageControllerItem.NAME, () -> new PackageControllerItem()); 23 | public static final RegistryObject PACKING_TAPE = register("packing_tape", () -> new Item(new Properties())); 24 | public static final RegistryObject STAMP = register("stamp", () -> new Item(new Properties())); 25 | 26 | public static RegistryObject register(String name, Supplier init) { 27 | return ITEM_DEFERRED.register(name, init); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/assets/endermail/lang/it_it.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Ender Mail Language File", 3 | "_comment": "Italian - ITALY", 4 | 5 | "_comment": "BLOCCHI", 6 | "block.endermail.package": "Pacco", 7 | "block.endermail.locker": "Armadietto per la posta", 8 | 9 | "_comment": "OGGETTI", 10 | "item.endermail.package_controller": "Il controller del pacco", 11 | "item.endermail.packing_tape": "Nastro da imballaggio", 12 | "item.endermail.stamp": "Francobollo", 13 | 14 | "_comment": "STRINGHE", 15 | "string.endermail.cancel": "Cancella", 16 | "string.endermail.close": "Chiudi", 17 | "string.endermail.confirm": "Conferma", 18 | "string.endermail.coordinates": "Coordinate", 19 | "string.endermail.deliver": "Fattorino", 20 | "string.endermail.delivered": "Spedito", 21 | "string.endermail.deliveredToLocker": "Spetito all'armadietto", 22 | "string.endermail.delivering": "Consegna", 23 | "string.endermail.deliveryDistance": "Distanza di consegna", 24 | "string.endermail.deliveryLocation": "Luogo della consegna", 25 | "string.endermail.error": "An error has occurred", 26 | "string.endermail.holdShift": "premi shift per guardare dentro", 27 | "string.endermail.lockerID": "Codice identificativo dell'Armadietto", 28 | "string.endermail.id": "Codice identificativo", 29 | "string.endermail.invalidLockerID": "Codice identificativo dell'Armadietto Invalido", 30 | "string.endermail.maxDistance": "Distanza massima", 31 | "string.endermail.setID": "Scegli codice identificativo", 32 | "string.endermail.status": "Stato", 33 | "string.endermail.tooFar": "Troppo lontano per essere consegnato", 34 | "string.endermail.undeliverable": "Non consegnabile" 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/util/RenderUtils.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.util; 2 | 3 | import com.chaosthedude.endermail.config.ConfigHandler; 4 | 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.Font; 7 | import net.minecraft.client.gui.GuiGraphics; 8 | import net.minecraftforge.api.distmarker.Dist; 9 | import net.minecraftforge.api.distmarker.OnlyIn; 10 | 11 | @OnlyIn(Dist.CLIENT) 12 | public class RenderUtils { 13 | 14 | private static final Minecraft mc = Minecraft.getInstance(); 15 | private static final Font font = mc.font; 16 | 17 | public static void drawStringLeft(GuiGraphics guiGraphics, String string, Font fontRenderer, int x, int y, int color) { 18 | guiGraphics.drawString(font, string, x, y, color); 19 | } 20 | 21 | public static void drawStringRight(GuiGraphics guiGraphics, String string, Font fontRenderer, int x, int y, int color) { 22 | guiGraphics.drawString(font, string, x - fontRenderer.width(string), y, color); 23 | } 24 | 25 | public static void drawConfiguredStringOnHUD(GuiGraphics guiGraphics, String string, int xOffset, int yOffset, int color, int relativeLineOffset) { 26 | final int lineOffset = ConfigHandler.CLIENT.lineOffset.get() + relativeLineOffset; 27 | yOffset += lineOffset * 9; 28 | if (ConfigHandler.CLIENT.overlaySide.get() == OverlaySide.LEFT) { 29 | drawStringLeft(guiGraphics, string, font, xOffset + 2, yOffset + 2, color); 30 | } else { 31 | drawStringRight(guiGraphics, string, font, mc.getWindow().getGuiScaledWidth() - xOffset - 2, yOffset + 2, color); 32 | } 33 | } 34 | 35 | public static void drawCenteredStringWithoutShadow(GuiGraphics guiGraphics, String string, int x, int y, int color) { 36 | guiGraphics.drawString(font, string, x - font.width(string) / 2, y, color, false); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/network/ConfigureLockerPacket.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.network; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import com.chaosthedude.endermail.block.LockerBlock; 6 | import com.chaosthedude.endermail.block.entity.LockerBlockEntity; 7 | 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.network.FriendlyByteBuf; 10 | import net.minecraft.world.level.block.entity.BlockEntity; 11 | import net.minecraftforge.network.NetworkEvent; 12 | 13 | public class ConfigureLockerPacket { 14 | 15 | private int lockerX; 16 | private int lockerY; 17 | private int lockerZ; 18 | private String lockerID; 19 | 20 | public ConfigureLockerPacket() { 21 | } 22 | 23 | public ConfigureLockerPacket(BlockPos lockerPos, String lockerID) { 24 | this.lockerX = lockerPos.getX(); 25 | this.lockerY = lockerPos.getY(); 26 | this.lockerZ = lockerPos.getZ(); 27 | this.lockerID = lockerID; 28 | } 29 | 30 | public ConfigureLockerPacket(FriendlyByteBuf buf) { 31 | lockerX = buf.readInt(); 32 | lockerY = buf.readInt(); 33 | lockerZ = buf.readInt(); 34 | 35 | lockerID = buf.readUtf(LockerBlock.MAX_ID_LENGTH); 36 | } 37 | 38 | public void toBytes(FriendlyByteBuf buf) { 39 | buf.writeInt(lockerX); 40 | buf.writeInt(lockerY); 41 | buf.writeInt(lockerZ); 42 | 43 | buf.writeUtf(lockerID); 44 | } 45 | 46 | public void handle(Supplier ctx) { 47 | ctx.get().enqueueWork(() -> { 48 | BlockEntity blockEntity = ctx.get().getSender().level().getBlockEntity(new BlockPos(lockerX, lockerY, lockerZ)); 49 | if (blockEntity instanceof LockerBlockEntity) { 50 | LockerBlockEntity lockerBlockEntity = (LockerBlockEntity) blockEntity; 51 | lockerBlockEntity.setLockerID(lockerID); 52 | 53 | } 54 | }); 55 | ctx.get().setPacketHandled(true); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/registry/EnderMailBlockEntities.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.registry; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.block.LockerBlock; 5 | import com.chaosthedude.endermail.block.PackageBlock; 6 | import com.chaosthedude.endermail.block.entity.LockerBlockEntity; 7 | import com.chaosthedude.endermail.block.entity.PackageBlockEntity; 8 | import com.google.common.base.Supplier; 9 | import com.mojang.datafixers.types.Type; 10 | 11 | import net.minecraft.Util; 12 | import net.minecraft.util.datafix.fixes.References; 13 | import net.minecraft.world.level.block.entity.BlockEntity; 14 | import net.minecraft.world.level.block.entity.BlockEntityType; 15 | import net.minecraftforge.registries.DeferredRegister; 16 | import net.minecraftforge.registries.ForgeRegistries; 17 | import net.minecraftforge.registries.RegistryObject; 18 | 19 | public class EnderMailBlockEntities { 20 | 21 | public static final DeferredRegister> BLOCK_ENTITY_DEFERRED = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, EnderMail.MODID); 22 | 23 | public static final RegistryObject> PACKAGE = register(PackageBlock.NAME, () -> BlockEntityType.Builder.of(PackageBlockEntity::new, EnderMailBlocks.PACKAGE.get())); 24 | public static final RegistryObject> LOCKER = register(LockerBlock.NAME, () -> BlockEntityType.Builder.of(LockerBlockEntity::new, EnderMailBlocks.LOCKER.get())); 25 | 26 | public static RegistryObject> register(String name, Supplier> initializer) { 27 | Type type = Util.fetchChoiceType(References.BLOCK_ENTITY, EnderMail.MODID + ":" + name); 28 | return BLOCK_ENTITY_DEFERRED.register(name, () -> initializer.get().build(type)); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/client/render/HeldPackageLayer.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.client.render; 2 | 3 | import com.chaosthedude.endermail.client.render.model.EnderMailmanModel; 4 | import com.chaosthedude.endermail.entity.EnderMailmanEntity; 5 | import com.chaosthedude.endermail.registry.EnderMailBlocks; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import com.mojang.math.Axis; 8 | 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.renderer.MultiBufferSource; 11 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 12 | import net.minecraft.client.renderer.texture.OverlayTexture; 13 | import net.minecraftforge.api.distmarker.Dist; 14 | import net.minecraftforge.api.distmarker.OnlyIn; 15 | import net.minecraftforge.client.model.data.ModelData; 16 | 17 | @OnlyIn(Dist.CLIENT) 18 | public class HeldPackageLayer extends RenderLayer { 19 | 20 | public HeldPackageLayer(EnderMailmanRenderer renderer) { 21 | super(renderer); 22 | } 23 | 24 | @Override 25 | public void render(PoseStack poseStack, MultiBufferSource buffer, int packedLight, EnderMailmanEntity entity, float p_116643_, float p_116644_, float p_116645_, float p_116646_, float p_116647_, float p_116648_) { 26 | if (entity.isCarryingPackage()) { 27 | poseStack.pushPose(); 28 | poseStack.translate(0.0D, 0.6875D, -0.75D); 29 | poseStack.mulPose(Axis.XP.rotationDegrees(20.0F)); 30 | poseStack.mulPose(Axis.YP.rotationDegrees(45.0F)); 31 | poseStack.translate(0.25D, 0.1875D, 0.25D); 32 | poseStack.scale(-0.5F, -0.5F, 0.5F); 33 | poseStack.mulPose(Axis.YP.rotationDegrees(90.0F)); 34 | Minecraft.getInstance().getBlockRenderer().renderSingleBlock(EnderMailBlocks.PACKAGE.get().getStampedState(), poseStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, null); 35 | poseStack.popPose(); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/data/LockerData.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.data; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.nbt.CompoundTag; 8 | import net.minecraft.nbt.ListTag; 9 | import net.minecraft.nbt.Tag; 10 | import net.minecraft.server.level.ServerLevel; 11 | import net.minecraft.world.level.saveddata.SavedData; 12 | import net.minecraft.world.level.storage.DimensionDataStorage; 13 | 14 | public class LockerData extends SavedData { 15 | 16 | public static final String ID = "lockers"; 17 | 18 | private final Map lockers = new HashMap(); 19 | 20 | public LockerData() { 21 | } 22 | 23 | public LockerData(CompoundTag tag) { 24 | ListTag lockerList = tag.getList("Lockers", Tag.TAG_COMPOUND); 25 | for (Tag t : lockerList) { 26 | CompoundTag lockerTag = (CompoundTag) t; 27 | String lockerID = lockerTag.getString("ID"); 28 | BlockPos pos = new BlockPos(lockerTag.getInt("X"), lockerTag.getInt("Y"), lockerTag.getInt("Z")); 29 | lockers.put(lockerID, pos); 30 | } 31 | } 32 | 33 | @Override 34 | public CompoundTag save(CompoundTag tag) { 35 | ListTag lockerList = new ListTag(); 36 | for (String lockerID : lockers.keySet()) { 37 | CompoundTag lockerTag = new CompoundTag(); 38 | lockerTag.putString("ID", lockerID); 39 | lockerTag.putInt("X", lockers.get(lockerID).getX()); 40 | lockerTag.putInt("Y", lockers.get(lockerID).getY()); 41 | lockerTag.putInt("Z", lockers.get(lockerID).getZ()); 42 | lockerList.add(lockerTag); 43 | } 44 | tag.put("Lockers", lockerList); 45 | return tag; 46 | } 47 | 48 | public String createLocker(String lockerID, BlockPos pos) { 49 | int suffixIndex = 2; 50 | String fixedLockerID = lockerID; 51 | while (lockerExists(fixedLockerID) && fixedLockerID.length() <= 12) { 52 | fixedLockerID = lockerID + suffixIndex; 53 | suffixIndex++; 54 | } 55 | if (fixedLockerID.length() > 12) { 56 | return ""; 57 | } 58 | lockers.put(fixedLockerID, pos); 59 | setDirty(); 60 | return fixedLockerID; 61 | } 62 | 63 | public boolean lockerExists(String lockerID) { 64 | return lockers.containsKey(lockerID); 65 | } 66 | 67 | public void removeLocker(String lockerID) { 68 | lockers.remove(lockerID); 69 | setDirty(); 70 | } 71 | 72 | public Map getLockers() { 73 | return lockers; 74 | } 75 | 76 | public static LockerData get(ServerLevel level) { 77 | DimensionDataStorage data = level.getDataStorage(); 78 | return (LockerData) data.computeIfAbsent(LockerData::new, LockerData::new, ID); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/network/StampPackagePacket.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.network; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import com.chaosthedude.endermail.block.LockerBlock; 6 | import com.chaosthedude.endermail.block.PackageBlock; 7 | import com.chaosthedude.endermail.registry.EnderMailItems; 8 | import com.chaosthedude.endermail.util.ItemUtils; 9 | 10 | import net.minecraft.core.BlockPos; 11 | import net.minecraft.network.FriendlyByteBuf; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraftforge.network.NetworkEvent; 14 | 15 | public class StampPackagePacket { 16 | 17 | private int packageX; 18 | private int packageY; 19 | private int packageZ; 20 | 21 | private int deliveryX; 22 | private int deliveryY; 23 | private int deliveryZ; 24 | 25 | private String lockerID; 26 | 27 | private boolean hasDeliveryPos; 28 | 29 | public StampPackagePacket() { 30 | } 31 | 32 | public StampPackagePacket(BlockPos packagePos, BlockPos deliveryPos, String lockerID, boolean hasDeliveryPos) { 33 | this.packageX = packagePos.getX(); 34 | this.packageY = packagePos.getY(); 35 | this.packageZ = packagePos.getZ(); 36 | 37 | this.deliveryX = deliveryPos.getX(); 38 | this.deliveryY = deliveryPos.getY(); 39 | this.deliveryZ = deliveryPos.getZ(); 40 | 41 | this.lockerID = lockerID; 42 | 43 | this.hasDeliveryPos = hasDeliveryPos; 44 | } 45 | 46 | public StampPackagePacket(FriendlyByteBuf buf) { 47 | packageX = buf.readInt(); 48 | packageY = buf.readInt(); 49 | packageZ = buf.readInt(); 50 | 51 | deliveryX = buf.readInt(); 52 | deliveryY = buf.readInt(); 53 | deliveryZ = buf.readInt(); 54 | 55 | lockerID = buf.readUtf(LockerBlock.MAX_ID_LENGTH); 56 | 57 | hasDeliveryPos = buf.readBoolean(); 58 | } 59 | 60 | public void toBytes(FriendlyByteBuf buf) { 61 | buf.writeInt(packageX); 62 | buf.writeInt(packageY); 63 | buf.writeInt(packageZ); 64 | 65 | buf.writeInt(deliveryX); 66 | buf.writeInt(deliveryY); 67 | buf.writeInt(deliveryZ); 68 | 69 | buf.writeUtf(lockerID); 70 | 71 | buf.writeBoolean(hasDeliveryPos); 72 | } 73 | 74 | public void handle(Supplier ctx) { 75 | ctx.get().enqueueWork(() -> { 76 | PackageBlock.stampPackage(ctx.get().getSender().level(), new BlockPos(packageX, packageY, packageZ), new BlockPos(deliveryX, deliveryY, deliveryZ), lockerID, hasDeliveryPos); 77 | ItemStack stampStack = ItemUtils.getHeldItem(ctx.get().getSender(), EnderMailItems.STAMP.get()); 78 | if (stampStack != null && !ctx.get().getSender().isCreative()) { 79 | stampStack.setCount(stampStack.getCount() - 1); 80 | } 81 | }); 82 | ctx.get().setPacketHandled(true); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/container/PackageMenu.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui.container; 2 | 3 | import com.chaosthedude.endermail.block.PackageBlock; 4 | import com.chaosthedude.endermail.config.ConfigHandler; 5 | import com.chaosthedude.endermail.registry.EnderMailContainers; 6 | 7 | import net.minecraft.world.Container; 8 | import net.minecraft.world.SimpleContainer; 9 | import net.minecraft.world.entity.player.Inventory; 10 | import net.minecraft.world.entity.player.Player; 11 | import net.minecraft.world.inventory.AbstractContainerMenu; 12 | import net.minecraft.world.inventory.Slot; 13 | import net.minecraft.world.item.ItemStack; 14 | import net.minecraftforge.registries.ForgeRegistries; 15 | 16 | public class PackageMenu extends AbstractContainerMenu { 17 | 18 | public static final String NAME = "package"; 19 | 20 | private final Container packageContainer; 21 | 22 | public PackageMenu(int windowId, Inventory playerInventory) { 23 | this(windowId, playerInventory, new SimpleContainer(PackageBlock.INVENTORY_SIZE)); 24 | } 25 | 26 | public PackageMenu(int windowId, Inventory playerInventory, Container packageContainer) { 27 | super(EnderMailContainers.PACKAGE_CONTAINER.get(), windowId); 28 | this.packageContainer = packageContainer; 29 | packageContainer.startOpen(playerInventory.player); 30 | for (int j = 0; j < packageContainer.getContainerSize(); ++j) { 31 | addSlot(new Slot(packageContainer, j, 44 + j * 18, 20) { 32 | @Override 33 | public boolean mayPlace(ItemStack stack) { 34 | return !ConfigHandler.GENERAL.packageContentsBlacklist.get().contains(ForgeRegistries.ITEMS.getKey(stack.getItem()).toString()); 35 | } 36 | }); 37 | } 38 | 39 | for (int l = 0; l < 3; ++l) { 40 | for (int k = 0; k < 9; ++k) { 41 | addSlot(new Slot(playerInventory, k + l * 9 + 9, 8 + k * 18, l * 18 + 51)); 42 | } 43 | } 44 | 45 | for (int i1 = 0; i1 < 9; ++i1) { 46 | addSlot(new Slot(playerInventory, i1, 8 + i1 * 18, 109)); 47 | } 48 | } 49 | 50 | @Override 51 | public boolean stillValid(Player player) { 52 | return packageContainer.stillValid(player); 53 | } 54 | 55 | @Override 56 | public ItemStack quickMoveStack(Player player, int index) { 57 | ItemStack stack = ItemStack.EMPTY; 58 | Slot slot = (Slot) slots.get(index); 59 | if (slot != null && slot.hasItem()) { 60 | ItemStack slotStack = slot.getItem(); 61 | stack = slotStack.copy(); 62 | 63 | if (index < packageContainer.getContainerSize()) { 64 | if (!moveItemStackTo(slotStack, packageContainer.getContainerSize(), slots.size(), true)) { 65 | return ItemStack.EMPTY; 66 | } 67 | } else if (!moveItemStackTo(slotStack, 0, packageContainer.getContainerSize(), false)) { 68 | return ItemStack.EMPTY; 69 | } 70 | 71 | if (slotStack.isEmpty()) { 72 | slot.set(ItemStack.EMPTY); 73 | } else { 74 | slot.setChanged(); 75 | } 76 | } 77 | 78 | return stack; 79 | } 80 | 81 | @Override 82 | public void removed(Player player) { 83 | super.removed(player); 84 | packageContainer.stopOpen(player); 85 | } 86 | 87 | //@Override 88 | //public ITextComponent getName() { 89 | // return new StringTextComponent(I18n.format("block.endermail.package")); 90 | //} 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/container/LockerMenu.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui.container; 2 | 3 | import com.chaosthedude.endermail.block.LockerBlock; 4 | import com.chaosthedude.endermail.registry.EnderMailContainers; 5 | import com.chaosthedude.endermail.registry.EnderMailItems; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.network.FriendlyByteBuf; 9 | import net.minecraft.world.Container; 10 | import net.minecraft.world.SimpleContainer; 11 | import net.minecraft.world.entity.player.Inventory; 12 | import net.minecraft.world.entity.player.Player; 13 | import net.minecraft.world.inventory.AbstractContainerMenu; 14 | import net.minecraft.world.inventory.Slot; 15 | import net.minecraft.world.item.ItemStack; 16 | 17 | public class LockerMenu extends AbstractContainerMenu { 18 | 19 | public static final String NAME = "locker"; 20 | 21 | private final Container lockerContainer; 22 | private String lockerID; 23 | private BlockPos lockerPos; 24 | 25 | public LockerMenu(int windowId, Inventory inventory, FriendlyByteBuf buf) { 26 | this(windowId, inventory, new SimpleContainer(LockerBlock.INVENTORY_SIZE), buf.readBlockPos(), buf.readUtf()); 27 | } 28 | 29 | public LockerMenu(int windowId, Inventory playerInventory, Container lockerContainer, BlockPos lockerPos, String lockerID) { 30 | super(EnderMailContainers.LOCKER_CONTAINER.get(), windowId); 31 | this.lockerContainer = lockerContainer; 32 | this.lockerPos = lockerPos; 33 | this.lockerID = lockerID; 34 | lockerContainer.startOpen(playerInventory.player); 35 | for (int j = 0; j < lockerContainer.getContainerSize(); ++j) { 36 | addSlot(new Slot(lockerContainer, j, 8 + j * 18, 20) { 37 | @Override 38 | public boolean mayPlace(ItemStack stack) { 39 | return stack.getItem() == EnderMailItems.PACKAGE.get(); 40 | } 41 | @Override 42 | public int getMaxStackSize() { 43 | return 1; 44 | } 45 | }); 46 | } 47 | 48 | for (int l = 0; l < 3; ++l) { 49 | for (int k = 0; k < 9; ++k) { 50 | addSlot(new Slot(playerInventory, k + l * 9 + 9, 8 + k * 18, l * 18 + 51)); 51 | } 52 | } 53 | 54 | for (int i1 = 0; i1 < 9; ++i1) { 55 | addSlot(new Slot(playerInventory, i1, 8 + i1 * 18, 109)); 56 | } 57 | broadcastChanges(); 58 | } 59 | 60 | @Override 61 | public boolean stillValid(Player player) { 62 | return lockerContainer.stillValid(player); 63 | } 64 | 65 | @Override 66 | public ItemStack quickMoveStack(Player player, int index) { 67 | ItemStack stack = ItemStack.EMPTY; 68 | Slot slot = (Slot) slots.get(index); 69 | if (slot != null && slot.hasItem()) { 70 | ItemStack slotStack = slot.getItem(); 71 | stack = slotStack.copy(); 72 | 73 | if (index < lockerContainer.getContainerSize()) { 74 | if (!moveItemStackTo(slotStack, lockerContainer.getContainerSize(), slots.size(), true)) { 75 | return ItemStack.EMPTY; 76 | } 77 | } else if (!moveItemStackTo(slotStack, 0, lockerContainer.getContainerSize(), false)) { 78 | return ItemStack.EMPTY; 79 | } 80 | 81 | if (slotStack.isEmpty()) { 82 | slot.set(ItemStack.EMPTY); 83 | } else { 84 | slot.setChanged(); 85 | } 86 | } 87 | 88 | return stack; 89 | } 90 | 91 | public String getLockerID() { 92 | return lockerID; 93 | } 94 | 95 | public BlockPos getLockerPos() { 96 | return lockerPos; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs=-Xmx3G 4 | org.gradle.daemon=false 5 | 6 | 7 | ## Environment Properties 8 | 9 | # The Minecraft version must agree with the Forge version to get a valid artifact 10 | minecraft_version=1.20.1 11 | # The Minecraft version range can use any release version of Minecraft as bounds. 12 | # Snapshots, pre-releases, and release candidates are not guaranteed to sort properly 13 | # as they do not follow standard versioning conventions. 14 | minecraft_version_range=[1.20.1,1.21) 15 | # The Forge version must agree with the Minecraft version to get a valid artifact 16 | forge_version=47.0.1 17 | # The Forge version range can use any version of Forge as bounds or match the loader version range 18 | forge_version_range=[47,) 19 | # The loader version range can only use the major version of Forge/FML as bounds 20 | loader_version_range=[47,) 21 | # The mapping channel to use for mappings. 22 | # The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"]. 23 | # Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin. 24 | # 25 | # | Channel | Version | | 26 | # |-----------|----------------------|--------------------------------------------------------------------------------| 27 | # | official | MCVersion | Official field/method names from Mojang mapping files | 28 | # | parchment | YYYY.MM.DD-MCVersion | Open community-sourced parameter names and javadocs layered on top of official | 29 | # 30 | # You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. 31 | # See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md 32 | # 33 | # Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge. 34 | # Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started 35 | mapping_channel=official 36 | # The mapping version to query from the mapping channel. 37 | # This must match the format required by the mapping channel. 38 | mapping_version=1.20.1 39 | 40 | 41 | ## Mod Properties 42 | 43 | # The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} 44 | # Must match the String constant located in the main mod class annotated with @Mod. 45 | mod_id=endermail 46 | # The human-readable display name for the mod. 47 | mod_name=Ender Mail 48 | # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. 49 | mod_license=CC BY-NC-SA 4.0 50 | # The mod version. See https://semver.org/ 51 | mod_version=1.20.1-1.2.9 52 | # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. 53 | # This should match the base package used for the mod sources. 54 | # See https://maven.apache.org/guides/mini/guide-naming-conventions.html 55 | mod_group_id=com.chaosthedude.endermail 56 | # The authors of the mod. This is a simple text string that is used for display purposes in the mod list. 57 | mod_authors=ChaosTheDude 58 | # The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. 59 | mod_description=Employ Endermen to deliver packages anywhere in the world. -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/config/ConfigHandler.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.chaosthedude.endermail.util.OverlaySide; 7 | 8 | import net.minecraftforge.common.ForgeConfigSpec; 9 | 10 | public class ConfigHandler { 11 | 12 | private static final ForgeConfigSpec.Builder GENERAL_BUILDER = new ForgeConfigSpec.Builder(); 13 | private static final ForgeConfigSpec.Builder CLIENT_BUILDER = new ForgeConfigSpec.Builder(); 14 | 15 | public static final General GENERAL = new General(GENERAL_BUILDER); 16 | public static final Client CLIENT = new Client(CLIENT_BUILDER); 17 | 18 | public static final ForgeConfigSpec GENERAL_SPEC = GENERAL_BUILDER.build(); 19 | public static final ForgeConfigSpec CLIENT_SPEC = CLIENT_BUILDER.build(); 20 | 21 | public static class General { 22 | public final ForgeConfigSpec.BooleanValue hideLockerLocation; 23 | public final ForgeConfigSpec.IntValue maxDeliveryDistance; 24 | public final ForgeConfigSpec.IntValue lockerDeliveryRadius; 25 | public final ForgeConfigSpec.BooleanValue lockerDeliveryRadiusIgnoresY; 26 | public final ForgeConfigSpec.BooleanValue logDeliveries; 27 | public final ForgeConfigSpec.ConfigValue> packageContentsBlacklist; 28 | 29 | General(ForgeConfigSpec.Builder builder) { 30 | String desc; 31 | builder.push("General"); 32 | 33 | desc = "Determines whether a locker\'s location will be hidden to the package sender."; 34 | hideLockerLocation = builder.comment(desc).define("hideLockerLocation", false); 35 | 36 | desc = "The maximum distance that packages can be delivered over. Set to -1 for no distance limit."; 37 | maxDeliveryDistance = builder.comment(desc).defineInRange("maxDeliveryDistance", -1, -1, 1000000); 38 | 39 | desc = "Packages with delivery locations within this radius of a locker will be delivered to the locker."; 40 | lockerDeliveryRadius = builder.comment(desc).defineInRange("lockerDeliveryRadius", 50, 0, 500); 41 | 42 | desc = "Determines whether a locker\'s delivery radius will ignore a package\'s delivery location\'s Y-coordinate."; 43 | lockerDeliveryRadiusIgnoresY = builder.comment(desc).define("lockerDeliveryRadiusIgnoresY", true); 44 | 45 | desc = "Determines whether package deliveries will be logged in the console."; 46 | logDeliveries = builder.comment(desc).define("logDeliveries", false); 47 | 48 | desc = "The list of items that are not allowed to be placed in packages. Ex: [\"endermail:package\"]"; 49 | packageContentsBlacklist = builder.comment(desc).define("packageContentsBlacklist", new ArrayList()); 50 | 51 | builder.pop(); 52 | } 53 | } 54 | 55 | public static class Client { 56 | public final ForgeConfigSpec.BooleanValue displayWithChatOpen; 57 | public final ForgeConfigSpec.IntValue lineOffset; 58 | public final ForgeConfigSpec.EnumValue overlaySide; 59 | 60 | Client(ForgeConfigSpec.Builder builder) { 61 | String desc; 62 | builder.push("Client"); 63 | 64 | desc = "Displays Package Controller information even while chat is open."; 65 | displayWithChatOpen = builder.comment(desc).define("displayWithChatOpen", true); 66 | 67 | desc = "The line offset for information rendered on the HUD."; 68 | lineOffset = builder.comment(desc).defineInRange("lineOffset", 1, 0, 50); 69 | 70 | desc = "The side for information rendered on the HUD. Ex: LEFT, RIGHT"; 71 | overlaySide = builder.comment(desc).defineEnum("overlaySide", OverlaySide.LEFT); 72 | 73 | builder.pop(); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/item/PackageControllerItem.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.item; 2 | 3 | import com.chaosthedude.endermail.util.ControllerState; 4 | import com.chaosthedude.endermail.util.ItemUtils; 5 | 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.world.InteractionHand; 8 | import net.minecraft.world.InteractionResultHolder; 9 | import net.minecraft.world.entity.player.Player; 10 | import net.minecraft.world.item.Item; 11 | import net.minecraft.world.item.ItemStack; 12 | import net.minecraft.world.level.Level; 13 | 14 | public class PackageControllerItem extends Item { 15 | 16 | public static final String NAME = "package_controller"; 17 | 18 | public PackageControllerItem() { 19 | super(new Properties()); 20 | } 21 | 22 | @Override 23 | public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { 24 | if (player.isCrouching()) { 25 | setState(player.getItemInHand(hand), ControllerState.DEFAULT); 26 | } 27 | return super.use(level, player, hand); 28 | } 29 | 30 | public void setState(ItemStack stack, ControllerState state) { 31 | if (ItemUtils.verifyNBT(stack)) { 32 | stack.getTag().putInt("State", state.getID()); 33 | } 34 | } 35 | 36 | public void setDeliveryPos(ItemStack stack, BlockPos pos) { 37 | if (ItemUtils.verifyNBT(stack)) { 38 | stack.getTag().putInt("DeliveryX", pos.getX()); 39 | stack.getTag().putInt("DeliveryY", pos.getY()); 40 | stack.getTag().putInt("DeliveryZ", pos.getZ()); 41 | } 42 | } 43 | 44 | public void setDeliveryDistance(ItemStack stack, int distance) { 45 | if (ItemUtils.verifyNBT(stack)) { 46 | stack.getTag().putInt("DeliveryDistance", distance); 47 | } 48 | } 49 | 50 | public void setMaxDistance(ItemStack stack, int distance) { 51 | if (ItemUtils.verifyNBT(stack)) { 52 | stack.getTag().putInt("MaxDistance", distance); 53 | } 54 | } 55 | 56 | public void setLockerID(ItemStack stack, String id) { 57 | if (ItemUtils.verifyNBT(stack)) { 58 | stack.getTag().putString("LockerID", id); 59 | } 60 | } 61 | 62 | public void setShowLockerLocation(ItemStack stack, boolean show) { 63 | if (ItemUtils.verifyNBT(stack)) { 64 | stack.getTag().putBoolean("ShowLockerLocation", show); 65 | } 66 | } 67 | 68 | public ControllerState getState(ItemStack stack) { 69 | if (ItemUtils.verifyNBT(stack)) { 70 | return ControllerState.fromID(stack.getTag().getInt("State")); 71 | } 72 | 73 | return null; 74 | } 75 | 76 | public BlockPos getDeliveryPos(ItemStack stack) { 77 | if (ItemUtils.verifyNBT(stack)) { 78 | return new BlockPos(stack.getTag().getInt("DeliveryX"), stack.getTag().getInt("DeliveryY"), stack.getTag().getInt("DeliveryZ")); 79 | } 80 | 81 | return null; 82 | } 83 | 84 | public int getDeliveryDistance(ItemStack stack) { 85 | if (ItemUtils.verifyNBT(stack)) { 86 | return stack.getTag().getInt("DeliveryDistance"); 87 | } 88 | 89 | return -1; 90 | } 91 | 92 | public int getMaxDistance(ItemStack stack) { 93 | if (ItemUtils.verifyNBT(stack)) { 94 | return stack.getTag().getInt("MaxDistance"); 95 | } 96 | 97 | return -1; 98 | } 99 | 100 | public String getLockerID(ItemStack stack) { 101 | if (ItemUtils.verifyNBT(stack)) { 102 | return stack.getTag().getString("LockerID"); 103 | } 104 | return ""; 105 | } 106 | 107 | public boolean shouldShowLockerLocation(ItemStack stack) { 108 | if (ItemUtils.verifyNBT(stack)) { 109 | return stack.getTag().getBoolean("ShowLockerLocation"); 110 | } 111 | return false; 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/LockerScreen.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.block.LockerBlock; 5 | import com.chaosthedude.endermail.gui.container.LockerMenu; 6 | import com.chaosthedude.endermail.network.ConfigureLockerPacket; 7 | 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.GuiGraphics; 10 | import net.minecraft.client.gui.components.EditBox; 11 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 12 | import net.minecraft.core.BlockPos; 13 | import net.minecraft.network.chat.Component; 14 | import net.minecraft.resources.ResourceLocation; 15 | import net.minecraft.world.entity.player.Inventory; 16 | import net.minecraftforge.api.distmarker.Dist; 17 | import net.minecraftforge.api.distmarker.OnlyIn; 18 | 19 | @OnlyIn(Dist.CLIENT) 20 | public class LockerScreen extends AbstractContainerScreen { 21 | 22 | private static final ResourceLocation TEXTURE = new ResourceLocation(EnderMail.MODID, "textures/gui/locker.png"); 23 | 24 | private EditBox idTextField; 25 | 26 | private BlockPos lockerPos; 27 | private String lockerID; 28 | 29 | public LockerScreen(LockerMenu containerLocker, Inventory playerInventory, Component title) { 30 | super(containerLocker, playerInventory, title); 31 | lockerPos = containerLocker.getLockerPos(); 32 | lockerID = containerLocker.getLockerID(); 33 | imageHeight = 133; 34 | inventoryLabelY = imageHeight - 94; 35 | } 36 | 37 | @Override 38 | protected void init() { 39 | super.init(); 40 | setupTextFields(); 41 | } 42 | 43 | @Override 44 | public void containerTick() { 45 | super.containerTick(); 46 | idTextField.tick(); 47 | } 48 | 49 | @Override 50 | public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { 51 | renderBackground(guiGraphics); 52 | super.render(guiGraphics, mouseX, mouseY, partialTicks); 53 | idTextField.render(guiGraphics, mouseX, mouseY, partialTicks); 54 | renderTooltip(guiGraphics, mouseX, mouseY); 55 | } 56 | 57 | @Override 58 | protected void renderLabels(GuiGraphics guiGraphics, int par2, int par3) { 59 | super.renderLabels(guiGraphics, par2, par3); 60 | guiGraphics.drawString(font, Component.translatable("string.endermail.id"), 75, titleLabelY, 4210752, false); 61 | } 62 | 63 | @Override 64 | public void onClose() { 65 | if (!idTextField.getValue().isEmpty() && !idTextField.getValue().equals(lockerID)) { 66 | EnderMail.network.sendToServer(new ConfigureLockerPacket(lockerPos, idTextField.getValue())); 67 | } 68 | super.onClose(); 69 | } 70 | 71 | @Override 72 | public boolean keyPressed(int par1, int par2, int par3) { 73 | if (par1 == 256 && shouldCloseOnEsc()) { 74 | onClose(); 75 | return true; 76 | } else if (idTextField.canConsumeInput()) { 77 | return idTextField.keyPressed(par1, par2, par3); 78 | } 79 | return super.keyPressed(par1, par2, par3); 80 | } 81 | 82 | @Override 83 | protected void renderBg(GuiGraphics guiGraphics, float partialTicks, int mouseX, int mouseY) { 84 | int i = (width - imageWidth) / 2; 85 | int j = (height - imageHeight) / 2; 86 | guiGraphics.blit(TEXTURE, i, j, 0, 0, imageWidth, imageHeight); 87 | } 88 | 89 | @Override 90 | public void resize(Minecraft mc, int par2, int par3) { 91 | String s = idTextField.getValue(); 92 | init(mc, par2, par3); 93 | idTextField.setValue(s); 94 | } 95 | 96 | private void setupTextFields() { 97 | clearWidgets(); 98 | idTextField = new EditBox(font, (width - imageWidth) / 2 + 75, (height - imageHeight) / 2 + 20, 80, 18, Component.literal("")); 99 | idTextField.setMaxLength(LockerBlock.MAX_ID_LENGTH); 100 | idTextField.setValue(lockerID); 101 | addWidget(idTextField); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/client/render/model/EnderMailmanModel.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.client.render.model; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.entity.EnderMailmanEntity; 5 | 6 | import net.minecraft.client.model.HumanoidModel; 7 | import net.minecraft.client.model.geom.ModelLayerLocation; 8 | import net.minecraft.client.model.geom.ModelPart; 9 | import net.minecraft.client.model.geom.PartPose; 10 | import net.minecraft.client.model.geom.builders.CubeDeformation; 11 | import net.minecraft.client.model.geom.builders.CubeListBuilder; 12 | import net.minecraft.client.model.geom.builders.LayerDefinition; 13 | import net.minecraft.client.model.geom.builders.MeshDefinition; 14 | import net.minecraft.client.model.geom.builders.PartDefinition; 15 | import net.minecraft.resources.ResourceLocation; 16 | import net.minecraftforge.api.distmarker.Dist; 17 | import net.minecraftforge.api.distmarker.OnlyIn; 18 | 19 | @OnlyIn(Dist.CLIENT) 20 | public class EnderMailmanModel extends HumanoidModel { 21 | 22 | public static final ModelLayerLocation LOCATION = new ModelLayerLocation(new ResourceLocation(EnderMail.MODID, EnderMailmanEntity.NAME), "main"); 23 | 24 | public boolean carrying; 25 | 26 | public EnderMailmanModel(ModelPart part) { 27 | super(part); 28 | } 29 | 30 | public static LayerDefinition createBodyLayer() { 31 | MeshDefinition mesh = HumanoidModel.createMesh(CubeDeformation.NONE, -14.0F); 32 | PartDefinition part = mesh.getRoot(); 33 | part.addOrReplaceChild("head", CubeListBuilder.create().texOffs(0, 0).addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F), PartPose.offset(0.0F, 0.0F, 0.0F)); 34 | part.addOrReplaceChild("hat", CubeListBuilder.create().texOffs(0, 38).addBox(-4.0F, -12.0F, -4.0F, 8.0F, 3.0F, 8.0F).texOffs(0, 49).addBox(-5.0F, -9.0F, -9.0F, 10.0F, 1.0F, 14.0F), PartPose.offset(0.0F, -14.0F, 0.0F)); 35 | part.addOrReplaceChild("body", CubeListBuilder.create().texOffs(32, 16).addBox(-4.0F, 0.0F, -2.0F, 8.0F, 12.0F, 4.0F), PartPose.offset(0.0F, -14.0F, 0.0F)); 36 | part.addOrReplaceChild("right_arm", CubeListBuilder.create().texOffs(56, 0).addBox(-1.0F, -2.0F, -1.0F, 2.0F, 30.0F, 2.0F), PartPose.offset(-5.0F, -12.0F, 0.0F)); 37 | part.addOrReplaceChild("left_arm", CubeListBuilder.create().texOffs(56, 0).mirror().addBox(-1.0F, -2.0F, -1.0F, 2.0F, 30.0F, 2.0F), PartPose.offset(5.0F, -12.0F, 0.0F)); 38 | part.addOrReplaceChild("right_leg", CubeListBuilder.create().texOffs(56, 0).addBox(-1.0F, 0.0F, -1.0F, 2.0F, 30.0F, 2.0F), PartPose.offset(-2.0F, -5.0F, 0.0F)); 39 | part.addOrReplaceChild("left_leg", CubeListBuilder.create().texOffs(56, 0).mirror().addBox(-1.0F, 0.0F, -1.0F, 2.0F, 30.0F, 2.0F), PartPose.offset(2.0F, -5.0F, 0.0F)); 40 | return LayerDefinition.create(mesh, 64, 64); 41 | } 42 | 43 | @Override 44 | public void setupAnim(EnderMailmanEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { 45 | super.setupAnim(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch); 46 | head.visible = true; 47 | hat.visible = true; 48 | body.xRot = 0.0F; 49 | body.y = -14.0F; 50 | body.z = -0.0F; 51 | rightLeg.xRot -= 0.0F; 52 | leftLeg.xRot -= 0.0F; 53 | rightArm.xRot = (float) ((double) rightArm.xRot * 0.5D); 54 | leftArm.xRot = (float) ((double) leftArm.xRot * 0.5D); 55 | rightLeg.xRot = (float) ((double) rightLeg.xRot * 0.5D); 56 | leftLeg.xRot = (float) ((double) leftLeg.xRot * 0.5D); 57 | if (rightArm.xRot > 0.4F) { 58 | rightArm.xRot = 0.4F; 59 | } 60 | 61 | if (leftArm.xRot > 0.4F) { 62 | leftArm.xRot = 0.4F; 63 | } 64 | 65 | if (rightArm.xRot < -0.4F) { 66 | rightArm.xRot = -0.4F; 67 | } 68 | 69 | if (leftArm.xRot < -0.4F) { 70 | leftArm.xRot = -0.4F; 71 | } 72 | 73 | if (rightLeg.xRot > 0.4F) { 74 | rightLeg.xRot = 0.4F; 75 | } 76 | 77 | if (leftLeg.xRot > 0.4F) { 78 | leftLeg.xRot = 0.4F; 79 | } 80 | 81 | if (rightLeg.xRot < -0.4F) { 82 | rightLeg.xRot = -0.4F; 83 | } 84 | 85 | if (leftLeg.xRot < -0.4F) { 86 | leftLeg.xRot = -0.4F; 87 | } 88 | 89 | if (carrying) { 90 | rightArm.xRot = -0.5F; 91 | leftArm.xRot = -0.5F; 92 | rightArm.zRot = 0.05F; 93 | leftArm.zRot = -0.05F; 94 | } 95 | 96 | rightLeg.z = 0.0F; 97 | leftLeg.z = 0.0F; 98 | rightLeg.y = -5.0F; 99 | leftLeg.y = -5.0F; 100 | head.z = -0.0F; 101 | head.y = -13.0F; 102 | hat.x = head.x; 103 | hat.y = head.y; 104 | hat.z = head.z; 105 | hat.xRot = head.xRot; 106 | hat.yRot = head.yRot; 107 | hat.zRot = head.zRot; 108 | 109 | rightArm.setPos(-5.0F, -12.0F, 0.0F); 110 | leftArm.setPos(5.0F, -12.0F, 0.0F); 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/EnderMail.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import com.chaosthedude.endermail.client.ClientEventHandler; 7 | import com.chaosthedude.endermail.config.ConfigHandler; 8 | import com.chaosthedude.endermail.gui.LockerScreen; 9 | import com.chaosthedude.endermail.gui.PackageScreen; 10 | import com.chaosthedude.endermail.item.PackageControllerItem; 11 | import com.chaosthedude.endermail.network.ConfigureLockerPacket; 12 | import com.chaosthedude.endermail.network.StampPackagePacket; 13 | import com.chaosthedude.endermail.registry.EnderMailBlockEntities; 14 | import com.chaosthedude.endermail.registry.EnderMailBlocks; 15 | import com.chaosthedude.endermail.registry.EnderMailContainers; 16 | import com.chaosthedude.endermail.registry.EnderMailEntities; 17 | import com.chaosthedude.endermail.registry.EnderMailItems; 18 | 19 | import net.minecraft.client.gui.screens.MenuScreens; 20 | import net.minecraft.client.multiplayer.ClientLevel; 21 | import net.minecraft.client.renderer.item.ClampedItemPropertyFunction; 22 | import net.minecraft.client.renderer.item.ItemProperties; 23 | import net.minecraft.resources.ResourceLocation; 24 | import net.minecraft.world.entity.LivingEntity; 25 | import net.minecraft.world.item.CreativeModeTabs; 26 | import net.minecraft.world.item.ItemStack; 27 | import net.minecraftforge.api.distmarker.Dist; 28 | import net.minecraftforge.api.distmarker.OnlyIn; 29 | import net.minecraftforge.common.MinecraftForge; 30 | import net.minecraftforge.event.BuildCreativeModeTabContentsEvent; 31 | import net.minecraftforge.eventbus.api.IEventBus; 32 | import net.minecraftforge.fml.DistExecutor; 33 | import net.minecraftforge.fml.ModLoadingContext; 34 | import net.minecraftforge.fml.common.Mod; 35 | import net.minecraftforge.fml.config.ModConfig; 36 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; 37 | import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; 38 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 39 | import net.minecraftforge.network.NetworkRegistry; 40 | import net.minecraftforge.network.simple.SimpleChannel; 41 | 42 | @Mod(EnderMail.MODID) 43 | public class EnderMail { 44 | 45 | public static final String MODID = "endermail"; 46 | 47 | public static final Logger LOGGER = LogManager.getLogger(MODID); 48 | 49 | public static SimpleChannel network; 50 | 51 | public EnderMail() { 52 | IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); 53 | 54 | EnderMailBlocks.BLOCK_DEFERRED.register(bus); 55 | EnderMailBlockEntities.BLOCK_ENTITY_DEFERRED.register(bus); 56 | EnderMailContainers.CONTAINER_DEFERRED.register(bus); 57 | EnderMailEntities.ENTITY_DEFERRED.register(bus); 58 | EnderMailItems.ITEM_DEFERRED.register(bus); 59 | 60 | bus.addListener(this::preInit); 61 | bus.addListener(this::buildCreativeTabContents); 62 | 63 | DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { 64 | FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientInit); 65 | }); 66 | 67 | ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ConfigHandler.GENERAL_SPEC); 68 | ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigHandler.CLIENT_SPEC); 69 | 70 | MinecraftForge.EVENT_BUS.register(this); 71 | } 72 | 73 | private void preInit(FMLCommonSetupEvent event) { 74 | network = NetworkRegistry.newSimpleChannel(new ResourceLocation(EnderMail.MODID, EnderMail.MODID), () -> "1.0", s -> true, s -> true); 75 | network.registerMessage(0, StampPackagePacket.class, StampPackagePacket::toBytes, StampPackagePacket::new, StampPackagePacket::handle); 76 | network.registerMessage(1, ConfigureLockerPacket.class, ConfigureLockerPacket::toBytes, ConfigureLockerPacket::new, ConfigureLockerPacket::handle); 77 | } 78 | 79 | private void buildCreativeTabContents(BuildCreativeModeTabContentsEvent event) { 80 | if (event.getTabKey() == CreativeModeTabs.TOOLS_AND_UTILITIES) { 81 | event.accept(new ItemStack(EnderMailItems.PACKAGE_CONTROLLER.get())); 82 | event.accept(new ItemStack(EnderMailItems.STAMP.get())); 83 | } else if (event.getTabKey() == CreativeModeTabs.INGREDIENTS) { 84 | event.accept(new ItemStack(EnderMailItems.PACKING_TAPE.get())); 85 | } else if (event.getTabKey() == CreativeModeTabs.FUNCTIONAL_BLOCKS) { 86 | event.accept(new ItemStack(EnderMailItems.PACKAGE.get())); 87 | event.accept(new ItemStack(EnderMailItems.LOCKER.get())); 88 | } 89 | } 90 | 91 | @OnlyIn(Dist.CLIENT) 92 | private void clientInit(FMLClientSetupEvent event) { 93 | MinecraftForge.EVENT_BUS.register(new ClientEventHandler()); 94 | MenuScreens.register(EnderMailContainers.PACKAGE_CONTAINER.get(), PackageScreen::new); 95 | MenuScreens.register(EnderMailContainers.LOCKER_CONTAINER.get(), LockerScreen::new); 96 | 97 | ItemProperties.register(EnderMailItems.PACKAGE_CONTROLLER.get(), new ResourceLocation(MODID, "state"), new ClampedItemPropertyFunction() { 98 | @Override 99 | public float unclampedCall(ItemStack stack, ClientLevel level, LivingEntity entity, int seed) { 100 | if (stack.getItem() == EnderMailItems.PACKAGE_CONTROLLER.get()) { 101 | PackageControllerItem packageController = (PackageControllerItem) stack.getItem(); 102 | return 0.1F * packageController.getState(stack).getID(); // Value must be between 0.0 and 1.0 103 | } 104 | return 0.0F; 105 | } 106 | }); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/block/LockerBlock.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.block; 2 | 3 | import com.chaosthedude.endermail.block.entity.LockerBlockEntity; 4 | import com.chaosthedude.endermail.registry.EnderMailBlocks; 5 | 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.Direction; 8 | import net.minecraft.server.level.ServerPlayer; 9 | import net.minecraft.world.Containers; 10 | import net.minecraft.world.InteractionHand; 11 | import net.minecraft.world.InteractionResult; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import net.minecraft.world.entity.player.Player; 14 | import net.minecraft.world.item.ItemStack; 15 | import net.minecraft.world.item.context.BlockPlaceContext; 16 | import net.minecraft.world.level.Level; 17 | import net.minecraft.world.level.block.BaseEntityBlock; 18 | import net.minecraft.world.level.block.Block; 19 | import net.minecraft.world.level.block.HorizontalDirectionalBlock; 20 | import net.minecraft.world.level.block.Mirror; 21 | import net.minecraft.world.level.block.RenderShape; 22 | import net.minecraft.world.level.block.Rotation; 23 | import net.minecraft.world.level.block.SoundType; 24 | import net.minecraft.world.level.block.entity.BlockEntity; 25 | import net.minecraft.world.level.block.state.BlockState; 26 | import net.minecraft.world.level.block.state.StateDefinition; 27 | import net.minecraft.world.level.block.state.properties.BooleanProperty; 28 | import net.minecraft.world.level.block.state.properties.DirectionProperty; 29 | import net.minecraft.world.level.block.state.properties.NoteBlockInstrument; 30 | import net.minecraft.world.level.material.MapColor; 31 | import net.minecraft.world.phys.BlockHitResult; 32 | import net.minecraftforge.network.NetworkHooks; 33 | 34 | public class LockerBlock extends BaseEntityBlock { 35 | 36 | public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; 37 | public static final BooleanProperty FILLED = BooleanProperty.create("filled"); 38 | 39 | public static final String NAME = "locker"; 40 | 41 | public static final int INVENTORY_SIZE = 3; 42 | public static final int MAX_ID_LENGTH = 12; 43 | 44 | public LockerBlock() { 45 | super(Properties.of().mapColor(MapColor.METAL).instrument(NoteBlockInstrument.IRON_XYLOPHONE).strength(4.0F, 6.0F).sound(SoundType.METAL)); 46 | registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH).setValue(FILLED, false)); 47 | } 48 | 49 | @Override 50 | protected void createBlockStateDefinition(StateDefinition.Builder builder) { 51 | builder.add(FACING, FILLED); 52 | } 53 | 54 | @Override 55 | public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult result) { 56 | if (level.isClientSide()) { 57 | return InteractionResult.SUCCESS; 58 | } 59 | if (!player.isCrouching()) { 60 | BlockEntity blockEntity = level.getBlockEntity(pos); 61 | if (blockEntity != null && blockEntity instanceof LockerBlockEntity) { 62 | LockerBlockEntity lockerBlockEntity = (LockerBlockEntity) blockEntity; 63 | NetworkHooks.openScreen((ServerPlayer) player, lockerBlockEntity, buf -> buf.writeBlockPos(pos).writeUtf(lockerBlockEntity.getLockerID())); 64 | } 65 | return InteractionResult.SUCCESS; 66 | } 67 | return InteractionResult.PASS; 68 | } 69 | 70 | @Override 71 | public void setPlacedBy(Level level, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { 72 | if (stack.hasCustomHoverName()) { 73 | BlockEntity blockEntity = level.getBlockEntity(pos); 74 | if (blockEntity instanceof LockerBlockEntity) { 75 | ((LockerBlockEntity) blockEntity).setLockerID(stack.getDisplayName().getString()); 76 | } 77 | } 78 | } 79 | 80 | @Override 81 | public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { 82 | if (!state.is(newState.getBlock())) { 83 | BlockEntity blockEntity = level.getBlockEntity(pos); 84 | if (blockEntity instanceof LockerBlockEntity) { 85 | LockerBlockEntity lockerBlockEntity = (LockerBlockEntity) blockEntity; 86 | lockerBlockEntity.removeData(); 87 | Containers.dropContents(level, pos, lockerBlockEntity); 88 | level.updateNeighbourForOutputSignal(pos, this); 89 | } 90 | super.onRemove(state, level, pos, newState, isMoving); 91 | } 92 | } 93 | 94 | @Override 95 | public BlockState getStateForPlacement(BlockPlaceContext context) { 96 | return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); 97 | } 98 | 99 | @Override 100 | public RenderShape getRenderShape(BlockState state) { 101 | return RenderShape.MODEL; 102 | } 103 | 104 | @Override 105 | public BlockState rotate(BlockState state, Rotation rot) { 106 | return state.setValue(FACING, rot.rotate((Direction) state.getValue(FACING))); 107 | } 108 | 109 | @Override 110 | public BlockState mirror(BlockState state, Mirror mirror) { 111 | return state.rotate(mirror.getRotation((Direction) state.getValue(FACING))); 112 | } 113 | 114 | @Override 115 | public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { 116 | return new LockerBlockEntity(pos, state); 117 | } 118 | 119 | public static void setFilled(boolean filled, Level level, BlockPos pos) { 120 | BlockState state = level.getBlockState(pos); 121 | if (state.getBlock() == EnderMailBlocks.LOCKER.get()) { 122 | if (state.getValue(FILLED) != filled) { 123 | level.setBlock(pos, state.setValue(FILLED, filled), 3); 124 | } 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/client/ClientEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.client; 2 | 3 | import com.chaosthedude.endermail.config.ConfigHandler; 4 | import com.chaosthedude.endermail.item.PackageControllerItem; 5 | import com.chaosthedude.endermail.registry.EnderMailItems; 6 | import com.chaosthedude.endermail.util.ControllerState; 7 | import com.chaosthedude.endermail.util.ItemUtils; 8 | import com.chaosthedude.endermail.util.RenderUtils; 9 | 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.gui.screens.ChatScreen; 12 | import net.minecraft.client.resources.language.I18n; 13 | import net.minecraft.world.entity.player.Player; 14 | import net.minecraft.world.item.ItemStack; 15 | import net.minecraftforge.api.distmarker.Dist; 16 | import net.minecraftforge.api.distmarker.OnlyIn; 17 | import net.minecraftforge.client.event.RenderGuiOverlayEvent; 18 | import net.minecraftforge.eventbus.api.SubscribeEvent; 19 | 20 | @OnlyIn(Dist.CLIENT) 21 | public class ClientEventHandler { 22 | 23 | private static final Minecraft mc = Minecraft.getInstance(); 24 | 25 | @SubscribeEvent 26 | public void onRenderTick(RenderGuiOverlayEvent.Post event) { 27 | if (mc.player != null && !mc.options.hideGui && !mc.options.renderDebug 28 | && (mc.screen == null || (ConfigHandler.CLIENT.displayWithChatOpen.get() && mc.screen instanceof ChatScreen))) { 29 | final Player player = mc.player; 30 | final ItemStack stack = ItemUtils.getHeldItem(player, EnderMailItems.PACKAGE_CONTROLLER.get()); 31 | if (stack != null && stack.getItem() instanceof PackageControllerItem) { 32 | final PackageControllerItem packageController = (PackageControllerItem) stack.getItem(); 33 | if (packageController.getState(stack) == ControllerState.DELIVERING) { 34 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.status"), 5, 0, 0xFFFFFF, 0); 35 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.delivering"), 5, 0, 0xAAAAAA, 1); 36 | } else if (packageController.getState(stack) == ControllerState.DELIVERED) { 37 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.status"), 5, 0, 0xFFFFFF, 0); 38 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.delivered"), 5, 0, 0xAAAAAA, 1); 39 | 40 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.coordinates"), 5, 0, 0xFFFFFF, 3); 41 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), packageController.getDeliveryPos(stack).getX() + " " + packageController.getDeliveryPos(stack).getY() + " " + packageController.getDeliveryPos(stack).getZ(), 5, 0, 0xAAAAAA, 4); 42 | } else if (packageController.getState(stack) == ControllerState.DELIVERED_TO_LOCKER) { 43 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.status"), 5, 0, 0xFFFFFF, 0); 44 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.deliveredToLocker"), 5, 0, 0xAAAAAA, 1); 45 | 46 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.lockerID"), 5, 0, 0xFFFFFF, 3); 47 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), packageController.getLockerID(stack), 5, 0, 0xAAAAAA, 4); 48 | 49 | if (packageController.shouldShowLockerLocation(stack)) { 50 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.coordinates"), 5, 0, 0xFFFFFF, 6); 51 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), packageController.getDeliveryPos(stack).getX() + " " + packageController.getDeliveryPos(stack).getY() + " " + packageController.getDeliveryPos(stack).getZ(), 5, 0, 0xAAAAAA, 7); 52 | } 53 | } else if (packageController.getState(stack) == ControllerState.UNDELIVERABLE) { 54 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.status"), 5, 0, 0xFFFFFF, 0); 55 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.undeliverable"), 5, 0, 0xAAAAAA, 1); 56 | } else if (packageController.getState(stack) == ControllerState.TOOFAR) { 57 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.status"), 5, 0, 0xFFFFFF, 0); 58 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.tooFar"), 5, 0, 0xAAAAAA, 1); 59 | 60 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.deliveryDistance"), 5, 0, 0xFFFFFF, 3); 61 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), String.valueOf(packageController.getDeliveryDistance(stack)), 5, 0, 0xAAAAAA, 4); 62 | 63 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.maxDistance"), 5, 0, 0xFFFFFF, 6); 64 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), String.valueOf(packageController.getMaxDistance(stack)), 5, 0, 0xAAAAAA, 7); 65 | } else if (packageController.getState(stack) == ControllerState.INVALID_LOCKER) { 66 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.status"), 5, 0, 0xFFFFFF, 0); 67 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.invalidLockerID"), 5, 0, 0xAAAAAA, 1); 68 | 69 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), I18n.get("string.endermail.lockerID"), 5, 0, 0xFFFFFF, 3); 70 | RenderUtils.drawConfiguredStringOnHUD(event.getGuiGraphics(), packageController.getLockerID(stack), 5, 0, 0xAAAAAA, 4); 71 | } 72 | } 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/block/entity/PackageBlockEntity.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.block.entity; 2 | 3 | import com.chaosthedude.endermail.block.PackageBlock; 4 | import com.chaosthedude.endermail.gui.container.PackageMenu; 5 | import com.chaosthedude.endermail.registry.EnderMailBlockEntities; 6 | import com.chaosthedude.endermail.registry.EnderMailItems; 7 | 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.core.NonNullList; 10 | import net.minecraft.nbt.CompoundTag; 11 | import net.minecraft.network.chat.Component; 12 | import net.minecraft.world.ContainerHelper; 13 | import net.minecraft.world.entity.player.Inventory; 14 | import net.minecraft.world.entity.player.Player; 15 | import net.minecraft.world.inventory.AbstractContainerMenu; 16 | import net.minecraft.world.item.ItemStack; 17 | import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; 18 | import net.minecraft.world.level.block.state.BlockState; 19 | 20 | public class PackageBlockEntity extends BaseContainerBlockEntity { 21 | 22 | public static final String NAME = "package"; 23 | 24 | private NonNullList contents = NonNullList.withSize(PackageBlock.INVENTORY_SIZE, ItemStack.EMPTY); 25 | private int deliveryX; 26 | private int deliveryY; 27 | private int deliveryZ; 28 | private String lockerID; 29 | private boolean hasDeliveryLocation; 30 | private boolean hasLockerID; 31 | 32 | public PackageBlockEntity(BlockPos pos, BlockState state) { 33 | super(EnderMailBlockEntities.PACKAGE.get(), pos, state); 34 | deliveryX = -1; 35 | deliveryY = -1; 36 | deliveryZ = -1; 37 | lockerID = ""; 38 | hasDeliveryLocation = false; 39 | } 40 | 41 | public PackageBlockEntity(NonNullList contents, BlockPos pos, BlockState state) { 42 | this(pos, state); 43 | this.contents = contents; 44 | } 45 | 46 | @Override 47 | public boolean isEmpty() { 48 | for (ItemStack stack : contents) { 49 | if (!stack.isEmpty()) { 50 | return false; 51 | } 52 | } 53 | 54 | return true; 55 | } 56 | 57 | @Override 58 | public void load(CompoundTag tag) { 59 | super.load(tag); 60 | contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); 61 | ContainerHelper.loadAllItems(tag, contents); 62 | 63 | deliveryX = tag.getInt("DeliveryX"); 64 | deliveryY = tag.getInt("DeliveryY"); 65 | deliveryZ = tag.getInt("DeliveryZ"); 66 | 67 | lockerID = tag.getString("LockerID"); 68 | 69 | hasDeliveryLocation = tag.getBoolean("HasDeliveryLocation"); 70 | hasLockerID = tag.getBoolean("HasLockerID"); 71 | } 72 | 73 | @Override 74 | public void saveAdditional(CompoundTag tag) { 75 | super.saveAdditional(tag); 76 | ContainerHelper.saveAllItems(tag, contents); 77 | 78 | tag.putInt("DeliveryX", deliveryX); 79 | tag.putInt("DeliveryY", deliveryY); 80 | tag.putInt("DeliveryZ", deliveryZ); 81 | 82 | tag.putString("LockerID", lockerID); 83 | 84 | tag.putBoolean("HasDeliveryLocation", hasDeliveryLocation); 85 | tag.putBoolean("HasLockerID", hasLockerID); 86 | } 87 | 88 | public CompoundTag writeItems(CompoundTag tag) { 89 | if (!contents.isEmpty()) { 90 | ContainerHelper.saveAllItems(tag, contents); 91 | } 92 | return tag; 93 | } 94 | 95 | @Override 96 | public int getMaxStackSize() { 97 | return 64; 98 | } 99 | 100 | @Override 101 | public void startOpen(Player player) { 102 | } 103 | 104 | @Override 105 | public void stopOpen(Player player) { 106 | } 107 | 108 | @Override 109 | public ItemStack getItem(int index) { 110 | return contents.get(index); 111 | } 112 | 113 | @Override 114 | public ItemStack removeItem(int index, int count) { 115 | return ContainerHelper.removeItem(contents, index, count); 116 | } 117 | 118 | @Override 119 | public ItemStack removeItemNoUpdate(int index) { 120 | return ContainerHelper.takeItem(contents, index); 121 | } 122 | 123 | @Override 124 | public void setItem(int index, ItemStack stack) { 125 | ItemStack itemstack = contents.get(index); 126 | boolean flag = !stack.isEmpty() && ItemStack.isSameItemSameTags(stack, itemstack); 127 | contents.set(index, stack); 128 | 129 | if (stack.getCount() > getMaxStackSize()) { 130 | stack.setCount(getMaxStackSize()); 131 | } 132 | } 133 | 134 | @Override 135 | public boolean stillValid(Player player) { 136 | if (level.getBlockEntity(worldPosition) != this) { 137 | return false; 138 | } else { 139 | return player.distanceToSqr((double) worldPosition.getX() + 0.5D, (double) worldPosition.getY() + 0.5D, (double) worldPosition.getZ() + 0.5D) <= 64.0D; 140 | } 141 | } 142 | 143 | @Override 144 | public boolean canPlaceItem(int index, ItemStack stack) { 145 | return stack.getItem() != EnderMailItems.PACKAGE.get(); 146 | } 147 | 148 | @Override 149 | public void clearContent() { 150 | contents.clear(); 151 | } 152 | 153 | @Override 154 | public AbstractContainerMenu createMenu(int windowId, Inventory playerInventory) { 155 | return new PackageMenu(windowId, playerInventory, this); 156 | } 157 | 158 | public NonNullList getContents() { 159 | return contents; 160 | } 161 | 162 | public void setDeliveryPos(BlockPos pos, boolean hasDeliveryLocation) { 163 | this.hasDeliveryLocation = hasDeliveryLocation; 164 | deliveryX = pos.getX(); 165 | deliveryY = pos.getY(); 166 | deliveryZ = pos.getZ(); 167 | } 168 | 169 | public void setLockerID(String lockerID) { 170 | hasLockerID = true; 171 | this.lockerID = lockerID; 172 | } 173 | 174 | public String getLockerID() { 175 | if (hasLockerID) { 176 | return lockerID; 177 | } 178 | return null; 179 | } 180 | 181 | public BlockPos getDeliveryPos() { 182 | return new BlockPos(deliveryX, deliveryY, deliveryZ); 183 | } 184 | 185 | public boolean hasDeliveryLocation() { 186 | return hasDeliveryLocation; 187 | } 188 | 189 | @Override 190 | public int getContainerSize() { 191 | return PackageBlock.INVENTORY_SIZE; 192 | } 193 | 194 | @Override 195 | protected Component getDefaultName() { 196 | return Component.translatable("block.endermail.package"); 197 | } 198 | 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/StampScreen.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui; 2 | 3 | import com.chaosthedude.endermail.EnderMail; 4 | import com.chaosthedude.endermail.block.LockerBlock; 5 | import com.chaosthedude.endermail.network.StampPackagePacket; 6 | import com.chaosthedude.endermail.util.RenderUtils; 7 | import com.mojang.blaze3d.systems.RenderSystem; 8 | 9 | import net.minecraft.client.gui.GuiGraphics; 10 | import net.minecraft.client.gui.components.Button; 11 | import net.minecraft.client.gui.screens.Screen; 12 | import net.minecraft.client.renderer.GameRenderer; 13 | import net.minecraft.client.resources.language.I18n; 14 | import net.minecraft.core.BlockPos; 15 | import net.minecraft.network.chat.Component; 16 | import net.minecraft.resources.ResourceLocation; 17 | import net.minecraft.world.entity.player.Player; 18 | import net.minecraft.world.level.Level; 19 | import net.minecraftforge.api.distmarker.Dist; 20 | import net.minecraftforge.api.distmarker.OnlyIn; 21 | 22 | @OnlyIn(Dist.CLIENT) 23 | public class StampScreen extends Screen { 24 | 25 | private static final ResourceLocation TEXTURE = new ResourceLocation(EnderMail.MODID, "textures/gui/stamp.png"); 26 | 27 | private Button confirmButton; 28 | private Button cancelButton; 29 | 30 | private StampTextField xTextField; 31 | private StampTextField yTextField; 32 | private StampTextField zTextField; 33 | 34 | private StampTextField lockerIDTextField; 35 | 36 | private Level level; 37 | private Player player; 38 | private BlockPos packagePos; 39 | 40 | private boolean errored; 41 | 42 | public StampScreen(Level level, Player player, BlockPos packagePos) { 43 | super(Component.literal("")); 44 | this.level = level; 45 | this.player = player; 46 | this.packagePos = packagePos; 47 | } 48 | 49 | @Override 50 | public void init() { 51 | setupWidgets(); 52 | } 53 | 54 | @Override 55 | public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { 56 | int xSize = 178; 57 | int ySize = 222; 58 | int i = (width - xSize) / 2; 59 | int j = (height - ySize) / 2; 60 | guiGraphics.blit(TEXTURE, i, j, 0, 0, xSize, ySize); 61 | if (errored) { 62 | RenderUtils.drawCenteredStringWithoutShadow(guiGraphics, I18n.get("string.endermail.error"), width / 2, height - 65, 0xAAAAAA); 63 | } 64 | 65 | RenderUtils.drawCenteredStringWithoutShadow(guiGraphics, I18n.get("string.endermail.deliveryLocation"), width / 2, height / 2 - 42, 0xAAAAAA); 66 | RenderUtils.drawCenteredStringWithoutShadow(guiGraphics, "X", (width / 2) - 45, height / 2 - 5, 0xAAAAAA); 67 | RenderUtils.drawCenteredStringWithoutShadow(guiGraphics, "Y", (width / 2) + 0, height / 2 - 5, 0xAAAAAA); 68 | RenderUtils.drawCenteredStringWithoutShadow(guiGraphics, "Z", (width / 2) + 45, height / 2 - 5, 0xAAAAAA); 69 | 70 | RenderUtils.drawCenteredStringWithoutShadow(guiGraphics, I18n.get("string.endermail.lockerID"), width / 2, height / 2 + 13, 0xAAAAAA); 71 | 72 | lockerIDTextField.render(guiGraphics, mouseX, mouseY, partialTicks); 73 | 74 | xTextField.render(guiGraphics, mouseX, mouseY, partialTicks); 75 | yTextField.render(guiGraphics, mouseX, mouseY, partialTicks); 76 | zTextField.render(guiGraphics, mouseX, mouseY, partialTicks); 77 | 78 | super.render(guiGraphics, mouseX, mouseY, partialTicks); 79 | } 80 | 81 | @Override 82 | public void tick() { 83 | super.tick(); 84 | lockerIDTextField.tick(); 85 | xTextField.tick(); 86 | yTextField.tick(); 87 | zTextField.tick(); 88 | confirmButton.active = (!lockerIDTextField.getValue().isEmpty() && xTextField.getValue().isEmpty() && yTextField.getValue().isEmpty() && zTextField.getValue().isEmpty()) || (isNumeric(xTextField.getValue()) && (yTextField.getValue().isEmpty() || isNumeric(yTextField.getValue())) && isNumeric(zTextField.getValue())); 89 | } 90 | 91 | private void setupWidgets() { 92 | clearWidgets(); 93 | cancelButton = addRenderableWidget(Button.builder(Component.translatable("string.endermail.cancel"), (onPress) -> { 94 | minecraft.setScreen(null); 95 | }).bounds(20, height - 40, 80, 20).build()); 96 | confirmButton = addRenderableWidget(Button.builder(Component.translatable("string.endermail.confirm"), (onPress) -> { 97 | try { 98 | String lockerID = lockerIDTextField.getValue(); 99 | int x = -1; 100 | int y = -1; 101 | int z = -1; 102 | if (lockerID.isEmpty() || (!xTextField.getValue().isEmpty() && !yTextField.getValue().isEmpty() && !zTextField.getValue().isEmpty())) { 103 | x = Integer.valueOf(xTextField.getValue()); 104 | if (!yTextField.getValue().isEmpty()) { 105 | y = Integer.valueOf(yTextField.getValue()); 106 | } 107 | z = Integer.valueOf(zTextField.getValue()); 108 | } 109 | BlockPos deliveryPos = new BlockPos(x, y, z); 110 | 111 | EnderMail.network.sendToServer(new StampPackagePacket(packagePos, deliveryPos, lockerID, !xTextField.getValue().isEmpty() && !zTextField.getValue().isEmpty())); 112 | minecraft.setScreen(null); 113 | } catch (NumberFormatException e) { 114 | errored = true; 115 | } 116 | }).bounds(width - 100, height - 40, 80, 20).build()); 117 | confirmButton.active = false; 118 | 119 | xTextField = addRenderableWidget(new StampTextField(font, (width / 2) - 65, height / 2 - 30, 40, 20, Component.literal(""))); 120 | yTextField = addRenderableWidget(new StampTextField(font, (width / 2) - 20, height / 2 - 30, 40, 20, Component.literal(""))); 121 | zTextField = addRenderableWidget(new StampTextField(font, (width / 2) + 25, height / 2 - 30, 40, 20, Component.literal(""))); 122 | 123 | lockerIDTextField = addRenderableWidget(new StampTextField(font, (width / 2) - 65, (height / 2) + 25, 130, 20, Component.literal(""))); 124 | lockerIDTextField.setMaxLength(LockerBlock.MAX_ID_LENGTH); 125 | 126 | setInitialFocus(xTextField); 127 | xTextField.setFocused(true); 128 | } 129 | 130 | public static boolean isNumeric(String s) { 131 | if (s == null || s.length() == 0) { 132 | return false; 133 | } 134 | int size = s.length(); 135 | for (int i = 0; i < size; i++) { 136 | if (!Character.isDigit(s.charAt(i)) && !(i == 0 && size > 1 && s.charAt(i) == '-')) { 137 | return false; 138 | } 139 | } 140 | return true; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/gui/StampTextField.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.gui; 2 | 3 | import net.minecraft.client.gui.Font; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | import net.minecraft.client.gui.components.EditBox; 6 | import net.minecraft.client.renderer.RenderType; 7 | import net.minecraft.network.chat.Component; 8 | import net.minecraft.util.Mth; 9 | 10 | public class StampTextField extends EditBox { 11 | 12 | private Font font; 13 | private Component label; 14 | private int labelColor = 0x808080; 15 | 16 | private boolean pseudoIsEnabled = true; 17 | private boolean pseudoEnableBackgroundDrawing = true; 18 | private int pseudoMaxStringLength = 32; 19 | private int pseudoLineScrollOffset; 20 | private int pseudoEnabledColor = 0x6e6e6e; 21 | private int pseudoDisabledColor = 0xcfcfcf; 22 | private int pseudoCursorCounter; 23 | private int pseudoSelectionEnd; 24 | 25 | public StampTextField(Font font, int x, int y, int width, int height, Component label) { 26 | super(font, x, y, width, height, label); 27 | this.font = font; 28 | this.label = label; 29 | } 30 | 31 | @Override 32 | public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { 33 | if (isVisible()) { 34 | if (pseudoEnableBackgroundDrawing) { 35 | final int color = (int) (255.0F * 0.25f); 36 | guiGraphics.fill(getX(), getY(), getX() + getWidth(), getY() + getHeight(), color / 2 << 24); 37 | } 38 | boolean showLabel = !isFocused() && getValue().isEmpty(); 39 | int i = showLabel ? labelColor : (pseudoIsEnabled ? pseudoEnabledColor : pseudoDisabledColor); 40 | int j = getCursorPosition() - pseudoLineScrollOffset; 41 | int k = pseudoSelectionEnd - pseudoLineScrollOffset; 42 | String text = showLabel ? label.getString() : getValue(); 43 | String s = font.plainSubstrByWidth(text.substring(pseudoLineScrollOffset), width, false); 44 | boolean flag = j >= 0 && j <= s.length(); 45 | boolean flag1 = isFocused() && pseudoCursorCounter / 6 % 2 == 0 && flag; 46 | int l = pseudoEnableBackgroundDrawing ? getX() + 4 : getX(); 47 | int i1 = pseudoEnableBackgroundDrawing ? getY() + (getHeight() - 8) / 2 : getY(); 48 | int j1 = l; 49 | 50 | if (k > s.length()) { 51 | k = s.length(); 52 | } 53 | 54 | if (!s.isEmpty()) { 55 | String s1 = flag ? s.substring(0, j) : s; 56 | j1 = guiGraphics.drawString(font, s1, l, i1, i, false); 57 | } 58 | 59 | boolean flag2 = getCursorPosition() < getValue().length() || getValue().length() >= pseudoMaxStringLength; 60 | int k1 = j1; 61 | 62 | if (!flag) { 63 | k1 = j > 0 ? l + width : l; 64 | } else if (flag2) { 65 | k1 = j1 - 1; 66 | --j1; 67 | } 68 | 69 | if (!s.isEmpty() && flag && j < s.length()) { 70 | j1 = guiGraphics.drawString(font, s.substring(j), j1, i1, i, false); 71 | } 72 | 73 | if (flag1) { 74 | if (flag2) { 75 | guiGraphics.fill(RenderType.guiOverlay(), k1, i1 - 1, k1 + 1, i1 + 1 + font.lineHeight, -3092272); 76 | } else { 77 | guiGraphics.drawString(font, "_", k1, i1, i, false); 78 | } 79 | } 80 | 81 | if (k != j) { 82 | int l1 = l + font.width(s.substring(0, k)); 83 | drawSelectionBox(guiGraphics, k1, i1 - 1, l1 - 1, i1 + 1 + font.lineHeight); 84 | } 85 | } 86 | } 87 | 88 | @Override 89 | public void setEditable(boolean enabled) { 90 | super.setEditable(enabled); 91 | pseudoIsEnabled = enabled; 92 | } 93 | 94 | @Override 95 | public void setTextColor(int color) { 96 | super.setTextColor(color); 97 | pseudoEnabledColor = color; 98 | } 99 | 100 | @Override 101 | public void setTextColorUneditable(int color) { 102 | super.setTextColorUneditable(color); 103 | pseudoDisabledColor = color; 104 | } 105 | 106 | @Override 107 | public void setFocused(boolean isFocused) { 108 | if (isFocused && !isFocused()) { 109 | pseudoCursorCounter = 0; 110 | } 111 | super.setFocused(isFocused); 112 | } 113 | 114 | @Override 115 | public void setBordered(boolean enableBackgroundDrawing) { 116 | super.setBordered(enableBackgroundDrawing); 117 | pseudoEnableBackgroundDrawing = enableBackgroundDrawing; 118 | } 119 | 120 | @Override 121 | public void setMaxLength(int length) { 122 | super.setMaxLength(length); 123 | pseudoMaxStringLength = length; 124 | } 125 | 126 | @Override 127 | public void tick() { 128 | super.tick(); 129 | pseudoCursorCounter++; 130 | } 131 | 132 | @Override 133 | public void setHighlightPos(int position) { 134 | super.setHighlightPos(position); 135 | int i = getValue().length(); 136 | pseudoSelectionEnd = Mth.clamp(position, 0, i); 137 | if (font != null) { 138 | if (pseudoLineScrollOffset > i) { 139 | pseudoLineScrollOffset = i; 140 | } 141 | 142 | int j = getInnerWidth(); 143 | String s = font.plainSubstrByWidth(getValue().substring(this.pseudoLineScrollOffset), j, false); 144 | int k = s.length() + pseudoLineScrollOffset; 145 | if (pseudoSelectionEnd == pseudoLineScrollOffset) { 146 | pseudoLineScrollOffset -= font.plainSubstrByWidth(getValue(), j, true).length(); 147 | } 148 | 149 | if (pseudoSelectionEnd > k) { 150 | pseudoLineScrollOffset += pseudoSelectionEnd - k; 151 | } else if (pseudoSelectionEnd <= pseudoLineScrollOffset) { 152 | pseudoLineScrollOffset -= pseudoLineScrollOffset - pseudoSelectionEnd; 153 | } 154 | 155 | pseudoLineScrollOffset = Mth.clamp(pseudoLineScrollOffset, 0, i); 156 | } 157 | } 158 | 159 | public void setLabel(Component label) { 160 | this.label = label; 161 | } 162 | 163 | public void setLabelColor(int labelColor) { 164 | this.labelColor = labelColor; 165 | } 166 | 167 | private void drawSelectionBox(GuiGraphics guiGraphics, int startX, int startY, int endX, int endY) { 168 | if (startX < endX) { 169 | int i = startX; 170 | startX = endX; 171 | endX = i; 172 | } 173 | 174 | if (startY < endY) { 175 | int j = startY; 176 | startY = endY; 177 | endY = j; 178 | } 179 | 180 | if (endX > getX() + getWidth()) { 181 | endX = getX() + getWidth(); 182 | } 183 | 184 | if (startX > getX() + getWidth()) { 185 | startX = getX() + getWidth(); 186 | } 187 | 188 | guiGraphics.fill(RenderType.guiTextHighlight(), startX, startY, endX, endY, -16776961); 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/block/entity/LockerBlockEntity.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.block.entity; 2 | 3 | import com.chaosthedude.endermail.block.LockerBlock; 4 | import com.chaosthedude.endermail.data.LockerData; 5 | import com.chaosthedude.endermail.gui.container.LockerMenu; 6 | import com.chaosthedude.endermail.registry.EnderMailBlockEntities; 7 | import com.chaosthedude.endermail.registry.EnderMailItems; 8 | 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.core.NonNullList; 11 | import net.minecraft.nbt.CompoundTag; 12 | import net.minecraft.network.chat.Component; 13 | import net.minecraft.server.level.ServerLevel; 14 | import net.minecraft.world.ContainerHelper; 15 | import net.minecraft.world.entity.player.Inventory; 16 | import net.minecraft.world.entity.player.Player; 17 | import net.minecraft.world.inventory.AbstractContainerMenu; 18 | import net.minecraft.world.item.ItemStack; 19 | import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; 20 | import net.minecraft.world.level.block.state.BlockState; 21 | 22 | public class LockerBlockEntity extends BaseContainerBlockEntity { 23 | 24 | public static final String NAME = "locker"; 25 | 26 | private NonNullList contents = NonNullList.withSize(LockerBlock.INVENTORY_SIZE, ItemStack.EMPTY); 27 | protected String lockerID; 28 | 29 | public LockerBlockEntity(BlockPos pos, BlockState state) { 30 | super(EnderMailBlockEntities.LOCKER.get(), pos, state); 31 | lockerID = ""; 32 | } 33 | 34 | public LockerBlockEntity(NonNullList contents, BlockPos pos, BlockState state) { 35 | this(pos, state); 36 | this.contents = contents; 37 | } 38 | 39 | @Override 40 | public int getContainerSize() { 41 | return LockerBlock.INVENTORY_SIZE; 42 | } 43 | 44 | @Override 45 | public boolean isEmpty() { 46 | for (ItemStack stack : contents) { 47 | if (!stack.isEmpty()) { 48 | return false; 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | 55 | @Override 56 | public void load(CompoundTag tag) { 57 | super.load(tag); 58 | contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); 59 | ContainerHelper.loadAllItems(tag, contents); 60 | lockerID = tag.getString("LockerID"); 61 | } 62 | 63 | @Override 64 | public void saveAdditional(CompoundTag tag) { 65 | super.saveAdditional(tag); 66 | ContainerHelper.saveAllItems(tag, contents); 67 | tag.putString("LockerID", lockerID); 68 | } 69 | 70 | public CompoundTag writeItems(CompoundTag compound) { 71 | if (!contents.isEmpty()) { 72 | ContainerHelper.saveAllItems(compound, contents); 73 | } 74 | return compound; 75 | } 76 | 77 | @Override 78 | public int getMaxStackSize() { 79 | return 64; 80 | } 81 | 82 | @Override 83 | public ItemStack getItem(int index) { 84 | return contents.get(index); 85 | } 86 | 87 | @Override 88 | public ItemStack removeItem(int index, int count) { 89 | ItemStack result = ContainerHelper.removeItem(contents, index, count); 90 | LockerBlock.setFilled(!isEmpty(), level, worldPosition); 91 | return result; 92 | } 93 | 94 | @Override 95 | public ItemStack removeItemNoUpdate(int index) { 96 | ItemStack result = ContainerHelper.takeItem(contents, index); 97 | LockerBlock.setFilled(!isEmpty(), level, worldPosition); 98 | return result; 99 | } 100 | 101 | @Override 102 | public void setItem(int index, ItemStack stack) { 103 | ItemStack itemstack = contents.get(index); 104 | boolean flag = !stack.isEmpty() && ItemStack.isSameItemSameTags(stack, itemstack); 105 | contents.set(index, stack); 106 | 107 | if (stack.getCount() > getMaxStackSize()) { 108 | stack.setCount(getMaxStackSize()); 109 | } 110 | LockerBlock.setFilled(!isEmpty(), level, worldPosition); 111 | } 112 | 113 | @Override 114 | public boolean stillValid(Player player) { 115 | if (level.getBlockEntity(worldPosition) != this) { 116 | return false; 117 | } else { 118 | return player.distanceToSqr((double) worldPosition.getX() + 0.5D, (double) worldPosition.getY() + 0.5D, (double) worldPosition.getZ() + 0.5D) <= 64.0D; 119 | } 120 | } 121 | 122 | @Override 123 | public boolean canPlaceItem(int index, ItemStack stack) { 124 | return stack.getItem() == EnderMailItems.PACKAGE.get(); 125 | } 126 | 127 | @Override 128 | public void clearContent() { 129 | contents.clear(); 130 | LockerBlock.setFilled(!isEmpty(), level, worldPosition); 131 | } 132 | 133 | @Override 134 | public AbstractContainerMenu createMenu(int windowID, Inventory playerInventory) { 135 | return new LockerMenu(windowID, playerInventory, this, worldPosition, lockerID); 136 | } 137 | 138 | @Override 139 | public Component getDisplayName() { 140 | return lockerID != null && !lockerID.isEmpty() ? Component.literal(lockerID) : Component.translatable("block.endermail.locker"); 141 | } 142 | 143 | public void removeData() { 144 | if (level instanceof ServerLevel) { 145 | ServerLevel serverLevel = (ServerLevel) level; 146 | LockerData data = LockerData.get(serverLevel); 147 | data.removeLocker(lockerID); 148 | } 149 | } 150 | 151 | public boolean isFull() { 152 | for (ItemStack stack : contents) { 153 | if (stack.isEmpty()) { 154 | return false; 155 | } 156 | } 157 | return true; 158 | } 159 | 160 | public boolean addPackage(ItemStack stack) { 161 | for (int i = 0; i < contents.size(); i++) { 162 | if (contents.get(i).isEmpty()) { 163 | setItem(i, stack); 164 | LockerBlock.setFilled(true, level, worldPosition); 165 | return true; 166 | } 167 | } 168 | return false; 169 | } 170 | 171 | public NonNullList getContents() { 172 | return contents; 173 | } 174 | 175 | public void setLockerID(String lockerID) { 176 | if (level instanceof ServerLevel) { 177 | ServerLevel serverLevel = (ServerLevel) level; 178 | LockerData data = LockerData.get(serverLevel); 179 | data.removeLocker(this.lockerID); 180 | this.lockerID = data.createLocker(lockerID, worldPosition); 181 | } 182 | } 183 | 184 | public String getLockerID() { 185 | return lockerID; 186 | } 187 | 188 | public boolean hasLockerID() { 189 | return lockerID != null && !lockerID.isEmpty(); 190 | } 191 | 192 | @Override 193 | protected Component getDefaultName() { 194 | return Component.translatable("block.endermail.locker"); 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | --------------------------------------------- 2 | VERSION 1.2.9 - 1.20.1, 1.20 3 | --------------------------------------------- 4 | 1.20.1: 5 | - Initial release for 1.20.1 6 | 1.20: 7 | - Initial release for 1.20 8 | 1.20.1, 1.20: 9 | - Allowed packages to be delivered on certain replaceable blocks, such as grass and snow 10 | - Fixed a bug in which a locker's ID was erased if it was the maximum 12 characters 11 | 12 | 13 | --------------------------------------------- 14 | VERSION 1.2.8 - 1.19.4 15 | --------------------------------------------- 16 | 1.19.4: 17 | - Initial release for 1.19.4 18 | - Fixed issue causing package controller texture to not update based on delivery success/failure 19 | 20 | 21 | --------------------------------------------- 22 | VERSION 1.2.7 - 1.19.3 23 | --------------------------------------------- 24 | 1.19.3: 25 | - Initial release for 1.19.3 26 | - Added Portuguese translations 27 | 28 | 29 | --------------------------------------------- 30 | VERSION 1.2.6 - 1.19.2, 1.19.1, 1.19 31 | --------------------------------------------- 32 | 1.19.2: 33 | - Initial release for 1.19.2 34 | 1.19.1: 35 | - Initial release for 1.19.1 36 | 1.19: 37 | - Made compatible with Forge 41.1 38 | 39 | 40 | --------------------------------------------- 41 | VERSION 1.2.5 - 1.19, 1.18.2 42 | --------------------------------------------- 43 | 1.19: 44 | - Initial release for 1.19 45 | 1.19, 1.18.2: 46 | - Fixed bug that prevented packages from being delivered to lockers placed before the server shut down 47 | - Updated drop rules for packages and lockers to respect game rules and player's game mode 48 | 49 | 50 | --------------------------------------------- 51 | VERSION 1.2.4 - 1.18.2, 1.17.1 52 | --------------------------------------------- 53 | 1.18.2, 1.17.1: 54 | - Fixed server crash upon initialization 55 | 56 | 57 | --------------------------------------------- 58 | VERSION 1.2.3 - 1.18.2, 1.17.1 59 | --------------------------------------------- 60 | 1.18.2: 61 | - Initial release for 1.18.2 62 | 1.17.1: 63 | - Initial release for 1.17.1 64 | - Added Korean translations 65 | - Fixed bug that caused multiple config backups to be saved 66 | 67 | 68 | --------------------------------------------- 69 | VERSION 1.2.2 - 1.16.5 70 | --------------------------------------------- 71 | 1.16.5: 72 | - Fixed incompatibility with RoadRunner and potentially other performance-enhancing mods 73 | 74 | 75 | --------------------------------------------- 76 | VERSION 1.2.1 - 1.16.5 77 | --------------------------------------------- 78 | 1.16.5: 79 | - Fixed bug in which lockers did not drop anything when broken 80 | - Fixed bug that caused packageContentsBlacklist config field to reset occasionally when starting the game 81 | - Added Italian translations 82 | - Added German translations 83 | - Added Polish translations 84 | 85 | 86 | --------------------------------------------- 87 | VERSION 1.2.0 - 1.16.5, 1.16.4 88 | --------------------------------------------- 89 | 1.16.5: 90 | - Initial release for 1.16.5 91 | 1.16.4: 92 | - Display error message on package controller after attempting to deliver a package to a nonexistent locker ID 93 | - Prevent locker IDs longer than the maximum 12 characters from being input on a stamp 94 | - Added a config field to determine which items are not allowed to be placed in packages 95 | - Added a config field that can be enabled to hide locker locations from package senders 96 | - Ender mailmen now teleport into the void after completing a delivery rather than immediately dying 97 | 98 | 99 | --------------------------------------------- 100 | VERSION 1.1.7 - 1.16.4 101 | --------------------------------------------- 102 | 1.16.4: 103 | - Fixed bug in which locker GUI was not able to be closed 104 | 105 | 106 | --------------------------------------------- 107 | VERSION 1.1.6 - 1.16.4 108 | --------------------------------------------- 109 | 1.16.4: 110 | - Initial release for 1.16.4 111 | - Added French translations 112 | 113 | 114 | --------------------------------------------- 115 | VERSION 1.1.5 - 1.16.3 116 | --------------------------------------------- 117 | 1.16.3: 118 | - Initial release for 1.16.3 119 | - Added Russian translations 120 | - Fixed incorrect Chinese translation 121 | 122 | 123 | --------------------------------------------- 124 | VERSION 1.1.4 - 1.16.2 125 | --------------------------------------------- 126 | 1.16.2: 127 | - Fixed crash that occurred with newer versions of Forge when opening the stamp GUI 128 | 129 | 130 | --------------------------------------------- 131 | VERSION 1.1.3 - 1.16.2, 1.16.1, 1.12.2 132 | --------------------------------------------- 133 | 1.16.2: 134 | - Initial release for 1.16.2 135 | 1.16.1: 136 | - Added Chinese translations 137 | - Fixed crash that occurred when moving a package controller between inventories during a delivery 138 | 139 | 140 | --------------------------------------------- 141 | VERSION 1.1.2 - 1.16.1 142 | --------------------------------------------- 143 | 1.16.1: 144 | - Fixed server crash upon startup when client-only initialization occurred on server 145 | 146 | 147 | --------------------------------------------- 148 | VERSION 1.1.1 - 1.16.1, 1.15.2, 1.12.2 149 | --------------------------------------------- 150 | 1.16.1, 1.15.2, 1.12.2: 151 | - Initial release for 1.16.1 152 | - Changed package and packing tape recipes to use tags for some items 153 | - Updated locker texture 154 | 155 | 156 | --------------------------------------------- 157 | VERSION 1.1.0 - 1.15.2, 1.12.2 158 | --------------------------------------------- 159 | 1.15.2, 1.12.2: 160 | - Added a Locker block with unique, configurable IDs that packages can be delivered to 161 | - Added a field to the stamp GUI to specify a locker ID to deliver to 162 | - Added config fields to specify the radius around lockers in which packages will be delivered to that locker 163 | - Added a config field to enable logging of package deliveries in the console 164 | 1.15.2: 165 | - Fixed a bug that caused Ender mailmen to despawn in peaceful mode 166 | 167 | 168 | --------------------------------------------- 169 | VERSION 1.0.3 - 1.15.2, 1.14.4, 1.12.2 170 | --------------------------------------------- 171 | 1.15.2, 1.14.4: 172 | - Fixed a bug that prevented packages from being opened on servers 173 | 1.12.2: 174 | - Fixed a bug that caused stamped packages to appear unstamped on servers 175 | 176 | 177 | --------------------------------------------- 178 | VERSION 1.0.2 - 1.15.2, 1.14.4, 1.12.2 179 | --------------------------------------------- 180 | 1.15.2, 1.14.4, 1.12.2: 181 | - Fixed a crash that occurred infrequently when moving a package controller around the inventory while a delivery was in progress 182 | 183 | 184 | --------------------------------------------- 185 | VERSION 1.0.1 - 1.12.2 186 | --------------------------------------------- 187 | 1.12.2: 188 | - Fixed a bug in the stamp GUI that allowed negative coordinates to be entered only if all previous text fields were filled 189 | 190 | 191 | --------------------------------------------- 192 | VERSION 1.0.0 - 1.15.2, 1.14.4, 1.12.2 193 | --------------------------------------------- 194 | 1.15.2: 195 | - Initial release for 1.15.2 196 | 1.14.4: 197 | - Initial release for 1.14.4 198 | 1.12.2: 199 | - Initial release for 1.12.2 -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/block/PackageBlock.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.block; 2 | 3 | import java.util.List; 4 | import java.util.Random; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | import com.chaosthedude.endermail.block.entity.PackageBlockEntity; 9 | import com.chaosthedude.endermail.config.ConfigHandler; 10 | import com.chaosthedude.endermail.data.LockerData; 11 | import com.chaosthedude.endermail.entity.EnderMailmanEntity; 12 | import com.chaosthedude.endermail.gui.ScreenWrapper; 13 | import com.chaosthedude.endermail.item.PackageControllerItem; 14 | import com.chaosthedude.endermail.registry.EnderMailEntities; 15 | import com.chaosthedude.endermail.registry.EnderMailItems; 16 | import com.chaosthedude.endermail.util.ControllerState; 17 | import com.chaosthedude.endermail.util.ItemUtils; 18 | 19 | import net.minecraft.ChatFormatting; 20 | import net.minecraft.client.gui.screens.Screen; 21 | import net.minecraft.core.BlockPos; 22 | import net.minecraft.core.Direction; 23 | import net.minecraft.core.NonNullList; 24 | import net.minecraft.nbt.CompoundTag; 25 | import net.minecraft.network.chat.Component; 26 | import net.minecraft.network.chat.MutableComponent; 27 | import net.minecraft.server.level.ServerLevel; 28 | import net.minecraft.server.level.ServerPlayer; 29 | import net.minecraft.world.ContainerHelper; 30 | import net.minecraft.world.InteractionHand; 31 | import net.minecraft.world.InteractionResult; 32 | import net.minecraft.world.entity.LivingEntity; 33 | import net.minecraft.world.entity.item.ItemEntity; 34 | import net.minecraft.world.entity.player.Player; 35 | import net.minecraft.world.item.ItemStack; 36 | import net.minecraft.world.item.TooltipFlag; 37 | import net.minecraft.world.item.context.BlockPlaceContext; 38 | import net.minecraft.world.level.BlockGetter; 39 | import net.minecraft.world.level.GameRules; 40 | import net.minecraft.world.level.Level; 41 | import net.minecraft.world.level.block.BaseEntityBlock; 42 | import net.minecraft.world.level.block.Block; 43 | import net.minecraft.world.level.block.HorizontalDirectionalBlock; 44 | import net.minecraft.world.level.block.Mirror; 45 | import net.minecraft.world.level.block.RenderShape; 46 | import net.minecraft.world.level.block.Rotation; 47 | import net.minecraft.world.level.block.SoundType; 48 | import net.minecraft.world.level.block.entity.BlockEntity; 49 | import net.minecraft.world.level.block.state.BlockState; 50 | import net.minecraft.world.level.block.state.StateDefinition; 51 | import net.minecraft.world.level.block.state.properties.BooleanProperty; 52 | import net.minecraft.world.level.block.state.properties.DirectionProperty; 53 | import net.minecraft.world.level.block.state.properties.NoteBlockInstrument; 54 | import net.minecraft.world.level.material.MapColor; 55 | import net.minecraft.world.phys.BlockHitResult; 56 | import net.minecraftforge.api.distmarker.Dist; 57 | import net.minecraftforge.api.distmarker.OnlyIn; 58 | import net.minecraftforge.network.NetworkHooks; 59 | 60 | public class PackageBlock extends BaseEntityBlock { 61 | 62 | public static final String NAME = "package"; 63 | 64 | public static final int INVENTORY_SIZE = 5; 65 | 66 | public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; 67 | public static final BooleanProperty STAMPED = BooleanProperty.create("stamped"); 68 | 69 | public PackageBlock() { 70 | super(Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(1.0F).sound(SoundType.WOOD)); 71 | registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH).setValue(STAMPED, false)); 72 | } 73 | 74 | @Override 75 | protected void createBlockStateDefinition(StateDefinition.Builder builder) { 76 | builder.add(FACING, STAMPED); 77 | } 78 | 79 | @Override 80 | public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { 81 | if (player.isSpectator()) { 82 | return InteractionResult.SUCCESS; 83 | } 84 | boolean holdingStamp = ItemUtils.isHolding(player, EnderMailItems.STAMP.get()); 85 | boolean holdingPackageController = ItemUtils.isHolding(player, EnderMailItems.PACKAGE_CONTROLLER.get()); 86 | if (level.isClientSide() && !isStamped(state) && player.getItemInHand(hand).getItem() == EnderMailItems.STAMP.get()) { 87 | ScreenWrapper.openStampScreen(level, player, pos); 88 | return InteractionResult.SUCCESS; 89 | } else if (level.isClientSide() && ((!isStamped(state) && !player.isCrouching()) || (isStamped(state) && (player.isCrouching() || (player.getItemInHand(hand) == ItemStack.EMPTY && !holdingStamp && !holdingPackageController))))) { 90 | return InteractionResult.SUCCESS; 91 | } else if (!level.isClientSide() && level instanceof ServerLevel) { 92 | ServerLevel serverLevel = (ServerLevel) level; 93 | if (isStamped(state) && player.getItemInHand(hand).getItem() == EnderMailItems.PACKAGE_CONTROLLER.get()) { 94 | ItemStack stack = ItemUtils.getHeldItem(player, EnderMailItems.PACKAGE_CONTROLLER.get()); 95 | PackageControllerItem packageController = (PackageControllerItem) stack.getItem(); 96 | BlockPos deliveryPos = getDeliveryPos(level, pos); 97 | String lockerID = getLockerID(level, pos); 98 | if (deliveryPos != null) { 99 | packageController.setDeliveryPos(stack, deliveryPos); 100 | int distanceToDelivery = (int) Math.sqrt(pos.distSqr(deliveryPos)); 101 | if (lockerID != null && !LockerData.get(serverLevel).lockerExists(lockerID) && !hasDeliveryLocation(level, pos)) { 102 | packageController.setState(stack, ControllerState.INVALID_LOCKER); 103 | packageController.setLockerID(stack, lockerID); 104 | } else if (ConfigHandler.GENERAL.maxDeliveryDistance.get() > -1 && distanceToDelivery > ConfigHandler.GENERAL.maxDeliveryDistance.get()) { 105 | packageController.setState(stack, ControllerState.TOOFAR); 106 | packageController.setDeliveryDistance(stack, distanceToDelivery); 107 | packageController.setMaxDistance(stack, ConfigHandler.GENERAL.maxDeliveryDistance.get()); 108 | } else { 109 | packageController.setState(stack, ControllerState.DELIVERING); 110 | EnderMailmanEntity enderMailman = new EnderMailmanEntity(EnderMailEntities.ENDER_MAILMAN.get(), level, pos, deliveryPos, lockerID, stack); 111 | level.addFreshEntity(enderMailman); 112 | } 113 | } 114 | return InteractionResult.SUCCESS; 115 | } else if (isStamped(state) && (player.isCrouching() || (player.getItemInHand(hand) == ItemStack.EMPTY && !holdingStamp && !holdingPackageController))) { 116 | setState(false, level, pos); 117 | return InteractionResult.SUCCESS; 118 | } else if (!isStamped(state) && !player.isCrouching() && !holdingStamp) { 119 | BlockEntity te = level.getBlockEntity(pos); 120 | if (te != null && te instanceof PackageBlockEntity) { 121 | NetworkHooks.openScreen((ServerPlayer) player, (PackageBlockEntity) te, pos); 122 | } 123 | return InteractionResult.SUCCESS; 124 | } 125 | } 126 | 127 | return InteractionResult.PASS; 128 | } 129 | 130 | @Override 131 | public void setPlacedBy(Level level, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { 132 | if (stack.hasCustomHoverName()) { 133 | BlockEntity blockEntity = level.getBlockEntity(pos); 134 | if (blockEntity instanceof PackageBlockEntity) { 135 | ((PackageBlockEntity) blockEntity).setCustomName(stack.getDisplayName()); 136 | } 137 | } 138 | } 139 | 140 | @Override 141 | public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { 142 | BlockEntity blockEntity = level.getBlockEntity(pos); 143 | if (blockEntity != null && blockEntity instanceof PackageBlockEntity) { 144 | PackageBlockEntity packageBlockEntity = (PackageBlockEntity) blockEntity; 145 | if (!level.isClientSide() && (!player.isCreative() || !packageBlockEntity.isEmpty()) && level.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) { 146 | ItemStack stackPackage = new ItemStack(EnderMailItems.PACKAGE.get()); 147 | CompoundTag stackTag = new CompoundTag(); 148 | CompoundTag itemTag = packageBlockEntity.writeItems(new CompoundTag()); 149 | if (!itemTag.isEmpty()) { 150 | stackTag.put("BlockEntityTag", itemTag); 151 | } 152 | if (!stackTag.isEmpty()) { 153 | stackPackage.setTag(stackTag); 154 | } 155 | 156 | if (packageBlockEntity.hasCustomName()) { 157 | stackPackage.setHoverName(packageBlockEntity.getDisplayName()); 158 | } 159 | 160 | ItemEntity itemEntity = new ItemEntity(level, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, stackPackage); 161 | itemEntity.setDefaultPickUpDelay(); 162 | level.addFreshEntity(itemEntity); 163 | } 164 | } 165 | super.playerWillDestroy(level, pos, state, player); 166 | } 167 | 168 | @Override 169 | public ItemStack getCloneItemStack(BlockGetter level, BlockPos pos, BlockState state) { 170 | ItemStack stack = super.getCloneItemStack(level, pos, state); 171 | BlockEntity blockEntity = level.getBlockEntity(pos); 172 | if (blockEntity != null && blockEntity instanceof PackageBlockEntity) { 173 | PackageBlockEntity packageBlockEntity = (PackageBlockEntity) blockEntity; 174 | CompoundTag stackTag = new CompoundTag(); 175 | CompoundTag itemTag = packageBlockEntity.writeItems(new CompoundTag()); 176 | if (!itemTag.isEmpty()) { 177 | stackTag.put("BlockEntityTag", itemTag); 178 | } 179 | if (!stackTag.isEmpty()) { 180 | stack.setTag(stackTag); 181 | } 182 | 183 | if (packageBlockEntity.hasCustomName()) { 184 | stack.setHoverName(packageBlockEntity.getDisplayName()); 185 | } 186 | } 187 | 188 | return stack; 189 | } 190 | 191 | @OnlyIn(Dist.CLIENT) 192 | @Override 193 | public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, TooltipFlag flag) { 194 | super.appendHoverText(stack, level, tooltip, flag); 195 | if (Screen.hasShiftDown()) { 196 | CompoundTag temp = stack.getTag(); 197 | if (temp != null && temp.contains("BlockEntityTag", 10)) { 198 | CompoundTag tag = temp.getCompound("BlockEntityTag"); 199 | if (tag.contains("Items", 9)) { 200 | NonNullList content = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY); 201 | ContainerHelper.loadAllItems(tag, content); 202 | for (ItemStack contentStack : content) { 203 | if (!contentStack.isEmpty()) { 204 | MutableComponent textComponent = contentStack.getDisplayName().copy(); 205 | textComponent.append(" x").append(String.valueOf(contentStack.getCount())).withStyle(ChatFormatting.GRAY); 206 | tooltip.add(textComponent); 207 | } 208 | } 209 | } 210 | } 211 | } else { 212 | tooltip.add(Component.translatable("string.endermail.holdShift").withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC)); 213 | } 214 | } 215 | 216 | @Override 217 | public BlockState getStateForPlacement(BlockPlaceContext context) { 218 | return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); 219 | } 220 | 221 | @Override 222 | public RenderShape getRenderShape(BlockState p_56255_) { 223 | return RenderShape.MODEL; 224 | } 225 | 226 | @Override 227 | public BlockState rotate(BlockState state, Rotation rotation) { 228 | return state.setValue(FACING, rotation.rotate((Direction) state.getValue(FACING))); 229 | } 230 | 231 | @Override 232 | public BlockState mirror(BlockState state, Mirror mirror) { 233 | return state.rotate(mirror.getRotation((Direction) state.getValue(FACING))); 234 | } 235 | 236 | @Override 237 | public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { 238 | return new PackageBlockEntity(pos, state); 239 | } 240 | 241 | public BlockState getStampedState() { 242 | return defaultBlockState().setValue(STAMPED, true); 243 | } 244 | 245 | public BlockState getRandomlyRotatedStampedState() { 246 | Direction[] directions = { Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH }; 247 | return defaultBlockState().setValue(STAMPED, true).setValue(FACING, directions[new Random().nextInt(4)]); 248 | } 249 | 250 | public boolean isStamped(BlockState state) { 251 | return state.getValue(STAMPED).booleanValue(); 252 | } 253 | 254 | public static void stampPackage(Level level, BlockPos packagePos, BlockPos deliveryPos, String lockerID, boolean hasDeliveryPos) { 255 | setState(true, level, packagePos); 256 | BlockEntity te = level.getBlockEntity(packagePos); 257 | if (te != null && te instanceof PackageBlockEntity) { 258 | PackageBlockEntity tePackage = (PackageBlockEntity) te; 259 | tePackage.setDeliveryPos(deliveryPos, hasDeliveryPos); 260 | tePackage.setLockerID(lockerID); 261 | } 262 | } 263 | 264 | public static BlockPos getDeliveryPos(Level level, BlockPos pos) { 265 | BlockEntity blockEntity = level.getBlockEntity(pos); 266 | if (blockEntity != null && blockEntity instanceof PackageBlockEntity) { 267 | PackageBlockEntity packageBlockEntity = (PackageBlockEntity) blockEntity; 268 | return packageBlockEntity.getDeliveryPos(); 269 | } 270 | return null; 271 | } 272 | 273 | public static String getLockerID(Level level, BlockPos pos) { 274 | BlockEntity blockEntity = level.getBlockEntity(pos); 275 | if (blockEntity != null && blockEntity instanceof PackageBlockEntity) { 276 | PackageBlockEntity packageBlockEntity = (PackageBlockEntity) blockEntity; 277 | return packageBlockEntity.getLockerID(); 278 | } 279 | return null; 280 | } 281 | 282 | public static boolean hasDeliveryLocation(Level level, BlockPos pos) { 283 | BlockEntity blockEntity = level.getBlockEntity(pos); 284 | if (blockEntity != null && blockEntity instanceof PackageBlockEntity) { 285 | PackageBlockEntity packageBlockEntity = (PackageBlockEntity) blockEntity; 286 | return packageBlockEntity.hasDeliveryLocation(); 287 | } 288 | return false; 289 | } 290 | 291 | public static void setState(boolean stamped, Level level, BlockPos pos) { 292 | BlockState state = level.getBlockState(pos); 293 | level.setBlock(pos, state.setValue(STAMPED, stamped), 3); 294 | } 295 | 296 | } 297 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | Creative Commons Corporation (�Creative Commons�) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an �as-is� basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. 4 | 5 | ### Using Creative Commons Public Licenses 6 | 7 | Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. 8 | 9 | * __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors). 10 | 11 | * __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor�s permission is not necessary for any reason�for example, because of any applicable exception or limitation to copyright�then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees). 12 | 13 | ## Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License 14 | 15 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. 16 | 17 | ### Section 1 � Definitions. 18 | 19 | a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. 20 | 21 | b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. 22 | 23 | c. __BY-NC-SA Compatible License__ means a license listed at [creativecommons.org/compatiblelicenses](http://creativecommons.org/compatiblelicenses), approved by Creative Commons as essentially the equivalent of this Public License. 24 | 25 | d. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 26 | 27 | e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. 28 | 29 | f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. 30 | 31 | g. __License Elements__ means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. 32 | 33 | h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. 34 | 35 | i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. 36 | 37 | h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. 38 | 39 | i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. 40 | 41 | j. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. 42 | 43 | k. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. 44 | 45 | l. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. 46 | 47 | ### Section 2 � Scope. 48 | 49 | a. ___License grant.___ 50 | 51 | 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: 52 | 53 | A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and 54 | 55 | B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 56 | 57 | 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 58 | 59 | 3. __Term.__ The term of this Public License is specified in Section 6(a). 60 | 61 | 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 62 | 63 | 5. __Downstream recipients.__ 64 | 65 | A. __Offer from the Licensor � Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. 66 | 67 | B. __Additional offer from the Licensor � Adapted Material.__ Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter�s License You apply. 68 | 69 | C. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 70 | 71 | 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). 72 | 73 | b. ___Other rights.___ 74 | 75 | 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 76 | 77 | 2. Patent and trademark rights are not licensed under this Public License. 78 | 79 | 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. 80 | 81 | ### Section 3 � License Conditions. 82 | 83 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 84 | 85 | a. ___Attribution.___ 86 | 87 | 1. If You Share the Licensed Material (including in modified form), You must: 88 | 89 | A. retain the following if it is supplied by the Licensor with the Licensed Material: 90 | 91 | i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); 92 | 93 | ii. a copyright notice; 94 | 95 | iii. a notice that refers to this Public License; 96 | 97 | iv. a notice that refers to the disclaimer of warranties; 98 | 99 | v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; 100 | 101 | B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and 102 | 103 | C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 104 | 105 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 106 | 107 | 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 108 | 109 | b. ___ShareAlike.___ 110 | 111 | In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 112 | 113 | 1. The Adapter�s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. 114 | 115 | 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 116 | 117 | 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. 118 | 119 | ### Section 4 � Sui Generis Database Rights. 120 | 121 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: 122 | 123 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; 124 | 125 | b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and 126 | 127 | c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. 128 | 129 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. 130 | 131 | ### Section 5 � Disclaimer of Warranties and Limitation of Liability. 132 | 133 | a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ 134 | 135 | b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ 136 | 137 | c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. 138 | 139 | ### Section 6 � Term and Termination. 140 | 141 | a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. 142 | 143 | b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 144 | 145 | 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 146 | 147 | 2. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 148 | 149 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. 150 | 151 | c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 152 | 153 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 154 | 155 | ### Section 7 � Other Terms and Conditions. 156 | 157 | a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 158 | 159 | b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. 160 | 161 | ### Section 8 � Interpretation. 162 | 163 | a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. 164 | 165 | b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 166 | 167 | c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. 168 | 169 | d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 170 | 171 | 172 | Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the �Licensor.� Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark �Creative Commons� or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. 173 | 174 | Creative Commons may be contacted at [creativecommons.org](http://creativecommons.org/). 175 | -------------------------------------------------------------------------------- /src/main/java/com/chaosthedude/endermail/entity/EnderMailmanEntity.java: -------------------------------------------------------------------------------- 1 | package com.chaosthedude.endermail.entity; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Random; 7 | 8 | import com.chaosthedude.endermail.EnderMail; 9 | import com.chaosthedude.endermail.block.PackageBlock; 10 | import com.chaosthedude.endermail.block.entity.LockerBlockEntity; 11 | import com.chaosthedude.endermail.block.entity.PackageBlockEntity; 12 | import com.chaosthedude.endermail.config.ConfigHandler; 13 | import com.chaosthedude.endermail.data.LockerData; 14 | import com.chaosthedude.endermail.item.PackageControllerItem; 15 | import com.chaosthedude.endermail.registry.EnderMailBlocks; 16 | import com.chaosthedude.endermail.registry.EnderMailItems; 17 | import com.chaosthedude.endermail.util.ControllerState; 18 | 19 | import net.minecraft.core.BlockPos; 20 | import net.minecraft.core.Direction; 21 | import net.minecraft.core.NonNullList; 22 | import net.minecraft.core.particles.ParticleTypes; 23 | import net.minecraft.nbt.CompoundTag; 24 | import net.minecraft.network.syncher.EntityDataAccessor; 25 | import net.minecraft.network.syncher.EntityDataSerializers; 26 | import net.minecraft.network.syncher.SynchedEntityData; 27 | import net.minecraft.server.level.ServerLevel; 28 | import net.minecraft.sounds.SoundEvent; 29 | import net.minecraft.sounds.SoundEvents; 30 | import net.minecraft.tags.FluidTags; 31 | import net.minecraft.world.ContainerHelper; 32 | import net.minecraft.world.damagesource.DamageSource; 33 | import net.minecraft.world.entity.EntityType; 34 | import net.minecraft.world.entity.LivingEntity; 35 | import net.minecraft.world.entity.ai.attributes.AttributeSupplier; 36 | import net.minecraft.world.entity.ai.attributes.Attributes; 37 | import net.minecraft.world.entity.ai.goal.Goal; 38 | import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; 39 | import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; 40 | import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; 41 | import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; 42 | import net.minecraft.world.entity.monster.Monster; 43 | import net.minecraft.world.entity.player.Player; 44 | import net.minecraft.world.item.ItemStack; 45 | import net.minecraft.world.level.Level; 46 | import net.minecraft.world.level.block.Blocks; 47 | import net.minecraft.world.level.block.entity.BlockEntity; 48 | import net.minecraft.world.level.block.state.BlockState; 49 | import net.minecraft.world.level.pathfinder.BlockPathTypes; 50 | 51 | public class EnderMailmanEntity extends Monster { 52 | 53 | public static final String NAME = "ender_mailman"; 54 | 55 | private static final EntityDataAccessor CARRYING_PACKAGE = SynchedEntityData.defineId(EnderMailmanEntity.class, EntityDataSerializers.BOOLEAN); 56 | private NonNullList contents = NonNullList.withSize(PackageBlock.INVENTORY_SIZE, ItemStack.EMPTY); 57 | private int lastCreepySound; 58 | private int timePickedUp; 59 | private int timeDelivered; 60 | private boolean isDelivering; 61 | private BlockPos startingPos; 62 | private BlockPos deliveryPos; 63 | private ItemStack packageController; 64 | 65 | public EnderMailmanEntity(EntityType entityType, Level level) { 66 | super(entityType, level); 67 | setMaxUpStep(1.0F); 68 | setPathfindingMalus(BlockPathTypes.WATER, -1.0F); 69 | } 70 | 71 | public EnderMailmanEntity(EntityType entityType, Level level, BlockPos startingPos, BlockPos deliveryPos, String lockerID, ItemStack packageController) { 72 | super(entityType, level); 73 | this.packageController = packageController; 74 | setPos(startingPos.getX() + getRandomOffset(), startingPos.getY(), startingPos.getZ() + getRandomOffset()); 75 | setStartingPos(startingPos); 76 | findDeliveryPos(lockerID, deliveryPos); 77 | } 78 | 79 | @Override 80 | protected void defineSynchedData() { 81 | super.defineSynchedData(); 82 | entityData.define(CARRYING_PACKAGE, Boolean.valueOf(false)); 83 | } 84 | 85 | @Override 86 | protected void registerGoals() { 87 | goalSelector.addGoal(1, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F)); 88 | goalSelector.addGoal(2, new EnderMailmanEntity.DeliverGoal(this)); 89 | goalSelector.addGoal(3, new EnderMailmanEntity.TakePackageGoal(this)); 90 | goalSelector.addGoal(4, new EnderMailmanEntity.DieGoal(this)); 91 | goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 8.0F)); 92 | goalSelector.addGoal(6, new RandomLookAroundGoal(this)); 93 | goalSelector.addGoal(7, new HurtByTargetGoal(this)); 94 | } 95 | 96 | public static AttributeSupplier.Builder createAttributes() { 97 | return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 40.0D).add(Attributes.MOVEMENT_SPEED, (double) 0.3F).add(Attributes.ATTACK_DAMAGE, 7.0D).add(Attributes.FOLLOW_RANGE, 64.0D); 98 | } 99 | 100 | @Override 101 | public void tick() { 102 | if (level().isClientSide()) { 103 | for (int i = 0; i < 2; ++i) { 104 | level().addParticle(ParticleTypes.PORTAL, getRandomX(0.5D), getRandomY() - 0.25D, getRandomZ(0.5D), (random.nextDouble() - 0.5D) * 2.0D, -random.nextDouble(), (random.nextDouble() - 0.5D) * 2.0D); 105 | } 106 | 107 | if (tickCount - timeDelivered > 100) { 108 | diePeacefully(); 109 | } 110 | } 111 | 112 | jumping = false; 113 | super.tick(); 114 | } 115 | 116 | @Override 117 | public boolean hurt(DamageSource source, float amount) { 118 | if (isInvulnerableTo(source)) { 119 | return false; 120 | } else if (source.isIndirect()) { 121 | for (int i = 0; i < 64; ++i) { 122 | if (this.teleportRandomly()) { 123 | return true; 124 | } 125 | } 126 | 127 | return false; 128 | } else { 129 | boolean flag = super.hurt(source, amount); 130 | if (!level().isClientSide() && !(source.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0) { 131 | teleportRandomly(); 132 | } 133 | 134 | return flag; 135 | } 136 | } 137 | 138 | @Override 139 | protected void customServerAiStep() { 140 | if (isInWaterRainOrBubble()) { 141 | hurt(damageSources().drown(), 1.0F); 142 | } 143 | 144 | if (level().isDay()) { 145 | float f = getLightLevelDependentMagicValue(); 146 | if (f > 0.5F && level().canSeeSky(blockPosition()) && random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F) { 147 | teleportRandomly(); 148 | } 149 | } 150 | 151 | super.customServerAiStep(); 152 | } 153 | 154 | protected SoundEvent getAmbientSound() { 155 | return SoundEvents.ENDERMAN_AMBIENT; 156 | } 157 | 158 | protected SoundEvent getHurtSound(DamageSource p_32527_) { 159 | return SoundEvents.ENDERMAN_HURT; 160 | } 161 | 162 | protected SoundEvent getDeathSound() { 163 | return SoundEvents.ENDERMAN_DEATH; 164 | } 165 | 166 | @Override 167 | public void addAdditionalSaveData(CompoundTag tag) { 168 | super.addAdditionalSaveData(tag); 169 | ContainerHelper.saveAllItems(tag, contents); 170 | 171 | tag.putInt("StartingX", startingPos.getX()); 172 | tag.putInt("StartingY", startingPos.getY()); 173 | tag.putInt("StartingZ", startingPos.getZ()); 174 | 175 | tag.putInt("DeliveryX", deliveryPos.getX()); 176 | tag.putInt("DeliveryY", deliveryPos.getY()); 177 | tag.putInt("DeliveryZ", deliveryPos.getZ()); 178 | 179 | tag.putBoolean("IsDelivering", isDelivering); 180 | tag.putBoolean("IsCarryingPackage", isCarryingPackage()); 181 | } 182 | 183 | @Override 184 | public void readAdditionalSaveData(CompoundTag tag) { 185 | super.readAdditionalSaveData(tag); 186 | ContainerHelper.loadAllItems(tag, contents); 187 | 188 | startingPos = new BlockPos(tag.getInt("StartingX"), tag.getInt("StartingY"), tag.getInt("StartingZ")); 189 | deliveryPos = new BlockPos(tag.getInt("DeliveryX"), tag.getInt("DeliveryY"), tag.getInt("DeliveryZ")); 190 | 191 | isDelivering = tag.getBoolean("IsDelivering"); 192 | entityData.set(CARRYING_PACKAGE, tag.getBoolean("IsCarryingPackage")); 193 | } 194 | 195 | @Override 196 | protected void dropEquipment() { 197 | super.dropEquipment(); 198 | for (ItemStack stack : contents) { 199 | spawnAtLocation(stack); 200 | } 201 | } 202 | 203 | @Override 204 | protected boolean shouldDespawnInPeaceful() { 205 | return false; 206 | } 207 | 208 | private boolean teleport(double x, double y, double z) { 209 | BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(x, y, z); 210 | while (pos.getY() > level().getMinBuildHeight() && !level().getBlockState(pos).blocksMotion()) { 211 | pos.move(Direction.DOWN); 212 | } 213 | 214 | BlockState state = level().getBlockState(pos); 215 | boolean flag = state.blocksMotion(); 216 | boolean flag1 = state.getFluidState().is(FluidTags.WATER); 217 | if (flag && !flag1) { 218 | boolean flag2 = randomTeleport(x, y, z, true); 219 | if (flag2 && !isSilent()) { 220 | level().playSound((Player) null, xo, yo, zo, SoundEvents.ENDERMAN_TELEPORT, getSoundSource(), 1.0F, 1.0F); 221 | playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); 222 | } 223 | return flag2; 224 | } else { 225 | return false; 226 | } 227 | } 228 | 229 | protected boolean teleportRandomly() { 230 | double x = getX() + (random.nextDouble() - 0.5D) * 64.0D; 231 | double y = getY() + (double) (random.nextInt(64) - 32); 232 | double z = getZ() + (random.nextDouble() - 0.5D) * 64.0D; 233 | return teleport(x, y, z); 234 | } 235 | 236 | private boolean canPlacePackage(Level level, BlockPos pos) { 237 | BlockState state = level.getBlockState(pos); 238 | BlockPos belowPos = pos.below(); 239 | BlockState belowState = level.getBlockState(belowPos); 240 | return !state.liquid() && state.canBeReplaced() && !belowState.canBeReplaced() && !belowState.is(Blocks.BEDROCK) && belowState.isCollisionShapeFullBlock(level, belowPos) && EnderMailBlocks.PACKAGE.get().defaultBlockState().canSurvive(level, pos); 241 | } 242 | 243 | private BlockPos findLocker(String lockerID) { 244 | if (level() instanceof ServerLevel) { 245 | ServerLevel serverLevel = (ServerLevel) level(); 246 | LockerData data = LockerData.get(serverLevel); 247 | return data.getLockers().get(lockerID); 248 | } 249 | return null; 250 | } 251 | 252 | private BlockPos findLockerNearPos(BlockPos pos) { 253 | if (level() instanceof ServerLevel) { 254 | ServerLevel serverLevel = (ServerLevel) level(); 255 | LockerData data = LockerData.get(serverLevel); 256 | for (String lockerID : data.getLockers().keySet()) { 257 | BlockPos lockerPos = data.getLockers().get(lockerID); 258 | if (ConfigHandler.GENERAL.lockerDeliveryRadiusIgnoresY.get()) { 259 | int deltaX = pos.getX() - lockerPos.getX(); 260 | int deltaZ = pos.getZ() - lockerPos.getZ(); 261 | int distanceSq = (deltaX * deltaX) + (deltaZ * deltaZ); 262 | if (distanceSq < ConfigHandler.GENERAL.lockerDeliveryRadius.get() * ConfigHandler.GENERAL.lockerDeliveryRadius.get()) { 263 | return lockerPos; 264 | } 265 | } else { 266 | if (pos.closerThan(lockerPos, ConfigHandler.GENERAL.lockerDeliveryRadius.get())) { 267 | return lockerPos; 268 | } 269 | } 270 | } 271 | } 272 | return null; 273 | } 274 | 275 | private int findValidDeliveryHeight(BlockPos pos, int maxHeightDifference) { 276 | if (pos != null) { 277 | int startY = pos.getY() <= 0 ? level().getSeaLevel() : pos.getY(); 278 | int upY = startY; 279 | int downY = startY; 280 | while (!(canPlacePackage(level(), new BlockPos(pos.getX(), upY, pos.getZ())) || canPlacePackage(level(), new BlockPos(pos.getX(), downY, pos.getZ()))) && (upY < 255 || downY > 1) && upY - startY < maxHeightDifference && startY - downY < maxHeightDifference) { 281 | upY++; 282 | downY--; 283 | } 284 | BlockPos upPos = new BlockPos(pos.getX(), upY, pos.getZ()); 285 | BlockPos downPos = new BlockPos(pos.getX(), downY, pos.getZ()); 286 | if (upY < 255 && canPlacePackage(level(), upPos)) { 287 | return upY; 288 | } 289 | if (downY > 1 && canPlacePackage(level(), downPos)) { 290 | return downY; 291 | } 292 | } 293 | return -1; 294 | } 295 | 296 | private void findDeliveryPos(String lockerID, BlockPos pos) { 297 | if (lockerID != null && !lockerID.isEmpty()) { 298 | BlockPos lockerPos = findLocker(lockerID); 299 | if (lockerPos != null) { 300 | setDeliveryPos(lockerPos); 301 | return; 302 | } 303 | } 304 | if (pos != null) { 305 | BlockPos validLocker = findLockerNearPos(pos); 306 | if (validLocker != null) { 307 | setDeliveryPos(validLocker); 308 | return; 309 | } 310 | int deliveryY = findValidDeliveryHeight(pos, 255); 311 | setDeliveryPos(new BlockPos(pos.getX(), deliveryY, pos.getZ())); 312 | } 313 | } 314 | 315 | public void diePeacefully() { 316 | teleportTo(getX(), -10, getZ()); 317 | hurt(damageSources().outOfBorder(), Float.MAX_VALUE); 318 | } 319 | 320 | public void setContents(NonNullList contents) { 321 | this.contents = contents; 322 | } 323 | 324 | public NonNullList getContents() { 325 | return contents; 326 | } 327 | 328 | public double getDistance(double x, double y, double z) { 329 | return Math.sqrt(distanceToSqr(x, y, z)); 330 | } 331 | 332 | public double getDistanceToDelivery() { 333 | return getDistance(deliveryPos.getX(), deliveryPos.getY(), deliveryPos.getZ()); 334 | } 335 | 336 | public double getDistanceToStart() { 337 | return getDistance(startingPos.getX(), startingPos.getY(), startingPos.getZ()); 338 | } 339 | 340 | public BlockPos getDeliveryPos() { 341 | return deliveryPos; 342 | } 343 | 344 | public void setDeliveryPos(BlockPos pos) { 345 | deliveryPos = pos; 346 | setDelivering(true); 347 | } 348 | 349 | public BlockPos getStartingPos() { 350 | return startingPos; 351 | } 352 | 353 | public void setPackageController(ItemStack packageController) { 354 | this.packageController = packageController; 355 | } 356 | 357 | public ItemStack getPackageController() { 358 | return packageController; 359 | } 360 | 361 | public PackageControllerItem getPackageControllerItem() { 362 | return (PackageControllerItem) packageController.getItem(); 363 | } 364 | 365 | public boolean hasPackageController() { 366 | return packageController != null && !packageController.isEmpty(); 367 | } 368 | 369 | public boolean isCarryingPackage() { 370 | return entityData.get(CARRYING_PACKAGE); 371 | } 372 | 373 | public boolean isDelivering() { 374 | return isDelivering; 375 | } 376 | 377 | public void setCarryingPackage(boolean carrying) { 378 | entityData.set(CARRYING_PACKAGE, carrying); 379 | } 380 | 381 | public void setDelivering(boolean delivering) { 382 | isDelivering = delivering; 383 | } 384 | 385 | public boolean shouldDeliverOnGround() { 386 | return canPlacePackage(level(), getDeliveryPos()); 387 | } 388 | 389 | public boolean shouldDeliverToLocker() { 390 | return level().getBlockState(deliveryPos).getBlock() == EnderMailBlocks.LOCKER.get(); 391 | } 392 | 393 | public void updateTimePickedUp() { 394 | timePickedUp = tickCount; 395 | } 396 | 397 | public int getTimePickedUp() { 398 | return timePickedUp; 399 | } 400 | 401 | public void updateTimeDelivered() { 402 | timeDelivered = tickCount; 403 | } 404 | 405 | public int getTimeDelivered() { 406 | return timeDelivered; 407 | } 408 | 409 | public void playEndermanSound() { 410 | if (tickCount >= lastCreepySound + 400) { 411 | lastCreepySound = tickCount; 412 | if (!isSilent()) { 413 | level().playLocalSound(this.getX(), this.getEyeY(), this.getZ(), SoundEvents.ENDERMAN_STARE, this.getSoundSource(), 2.5F, 1.0F, false); 414 | } 415 | } 416 | } 417 | 418 | public double getRandomOffset() { 419 | return random.nextDouble() * 2 * (random.nextBoolean() ? 1 : -1); 420 | } 421 | 422 | public void teleportToDeliveryPos() { 423 | double x = getDeliveryPos().getX() + getRandomOffset(); 424 | double y = getDeliveryPos().getY(); 425 | double z = getDeliveryPos().getZ() + getRandomOffset(); 426 | teleportTo(x, y, z); 427 | } 428 | 429 | public void teleportToStartingPos() { 430 | double x = getStartingPos().getX() + getRandomOffset(); 431 | double y = getStartingPos().getY(); 432 | double z = getStartingPos().getZ() + getRandomOffset(); 433 | teleportTo(x, y, z); 434 | } 435 | 436 | public boolean isAtStartingPos() { 437 | return blockPosition() == startingPos; 438 | } 439 | 440 | public void setStartingPos(BlockPos pos) { 441 | startingPos = pos; 442 | } 443 | 444 | public ItemStack getPackageStack() { 445 | ItemStack stackPackage = new ItemStack(EnderMailItems.PACKAGE.get()); 446 | CompoundTag stackTag = new CompoundTag(); 447 | CompoundTag itemTag = new CompoundTag(); 448 | if (!contents.isEmpty()) { 449 | itemTag = ContainerHelper.saveAllItems(itemTag, contents); 450 | } 451 | if (!itemTag.isEmpty()) { 452 | stackTag.put("BlockEntityTag", itemTag); 453 | } 454 | if (!stackTag.isEmpty()) { 455 | stackPackage.setTag(stackTag); 456 | } 457 | return stackPackage; 458 | } 459 | 460 | static class DeliverGoal extends Goal { 461 | private final EnderMailmanEntity enderMailman; 462 | 463 | public DeliverGoal(EnderMailmanEntity enderMailman) { 464 | this.enderMailman = enderMailman; 465 | } 466 | 467 | @Override 468 | public boolean canUse() { 469 | return enderMailman.isDelivering() && enderMailman.isCarryingPackage(); 470 | } 471 | 472 | @Override 473 | public void tick() { 474 | if (enderMailman.tickCount - enderMailman.getTimePickedUp() >= 100) { 475 | boolean delivered = false; 476 | if (enderMailman.shouldDeliverOnGround()) { 477 | enderMailman.teleportToDeliveryPos(); 478 | BlockState newBlockState = EnderMailBlocks.PACKAGE.get().getRandomlyRotatedStampedState(); 479 | enderMailman.level().setBlock(enderMailman.getDeliveryPos(), newBlockState, 3); 480 | enderMailman.level().setBlockEntity(new PackageBlockEntity(enderMailman.getContents(), enderMailman.getDeliveryPos(), newBlockState)); 481 | if (enderMailman.hasPackageController()) { 482 | enderMailman.getPackageControllerItem().setState(enderMailman.packageController, ControllerState.DELIVERED); 483 | enderMailman.getPackageControllerItem().setDeliveryPos(enderMailman.packageController, enderMailman.getDeliveryPos()); 484 | } 485 | if (ConfigHandler.GENERAL.logDeliveries.get()) { 486 | EnderMail.LOGGER.info("Delivered package to " + enderMailman.getDeliveryPos().getX() + ", " + enderMailman.getDeliveryPos().getY() + ", " + enderMailman.getDeliveryPos().getZ()); 487 | } 488 | delivered = true; 489 | } else if (enderMailman.shouldDeliverToLocker()) { 490 | BlockEntity blockEntity = enderMailman.level().getBlockEntity(enderMailman.getDeliveryPos()); 491 | if (blockEntity != null && blockEntity instanceof LockerBlockEntity) { 492 | enderMailman.teleportToDeliveryPos(); 493 | LockerBlockEntity lockerBlockEntity = (LockerBlockEntity) blockEntity; 494 | ItemStack stackPackage = enderMailman.getPackageStack(); 495 | boolean putInLocker = lockerBlockEntity.addPackage(stackPackage); 496 | if (putInLocker) { 497 | if (enderMailman.hasPackageController()) { 498 | enderMailman.getPackageControllerItem().setState(enderMailman.packageController, ControllerState.DELIVERED_TO_LOCKER); 499 | enderMailman.getPackageControllerItem().setLockerID(enderMailman.packageController, lockerBlockEntity.getLockerID()); 500 | enderMailman.getPackageControllerItem().setDeliveryPos(enderMailman.packageController, enderMailman.getDeliveryPos()); 501 | enderMailman.getPackageControllerItem().setShowLockerLocation(enderMailman.packageController, !ConfigHandler.GENERAL.hideLockerLocation.get()); 502 | } 503 | if (ConfigHandler.GENERAL.logDeliveries.get()) { 504 | EnderMail.LOGGER.info("Delivered package to locker " + lockerBlockEntity.getLockerID() + " at " + enderMailman.getDeliveryPos().getX() + ", " + enderMailman.getDeliveryPos().getY() + ", " + enderMailman.getDeliveryPos().getZ()); 505 | } 506 | delivered = true; 507 | } else { 508 | int y = -1; 509 | List directions = new ArrayList(Arrays.asList(Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH)); 510 | while (y < 0 && !directions.isEmpty()) { 511 | Direction randomDirection = directions.get(new Random().nextInt(directions.size())); 512 | directions.remove(randomDirection); 513 | BlockPos newDeliveryPos = enderMailman.getDeliveryPos().relative(randomDirection); 514 | y = enderMailman.findValidDeliveryHeight(newDeliveryPos, 8); 515 | if (y > 0) { 516 | newDeliveryPos = new BlockPos(newDeliveryPos.getX(), y, newDeliveryPos.getZ()); 517 | BlockState newBlockState = EnderMailBlocks.PACKAGE.get().getRandomlyRotatedStampedState(); 518 | enderMailman.level().setBlock(newDeliveryPos, newBlockState, 3); 519 | enderMailman.level().setBlockEntity(new PackageBlockEntity(enderMailman.getContents(), newDeliveryPos, newBlockState)); 520 | if (enderMailman.hasPackageController()) { 521 | enderMailman.getPackageControllerItem().setState(enderMailman.packageController, ControllerState.DELIVERED); 522 | enderMailman.getPackageControllerItem().setDeliveryPos(enderMailman.packageController, newDeliveryPos); 523 | } 524 | if (ConfigHandler.GENERAL.logDeliveries.get()) { 525 | EnderMail.LOGGER.info("Delivered package to " + newDeliveryPos.getX() + ", " + newDeliveryPos.getY() + ", " + newDeliveryPos.getZ() + " near locker " + lockerBlockEntity.getLockerID()); 526 | } 527 | delivered = true; 528 | } 529 | } 530 | } 531 | } 532 | } 533 | if (!delivered) { 534 | enderMailman.teleportToStartingPos(); 535 | BlockState newBlockState = EnderMailBlocks.PACKAGE.get().getRandomlyRotatedStampedState(); 536 | enderMailman.level().setBlock(enderMailman.getStartingPos(), newBlockState, 3); 537 | enderMailman.level().setBlockEntity(new PackageBlockEntity(enderMailman.getContents(), enderMailman.getStartingPos(), newBlockState)); 538 | if (enderMailman.hasPackageController()) { 539 | enderMailman.getPackageControllerItem().setState(enderMailman.packageController, ControllerState.UNDELIVERABLE); 540 | } 541 | } 542 | 543 | enderMailman.updateTimeDelivered(); 544 | enderMailman.setContents(NonNullList.withSize(PackageBlock.INVENTORY_SIZE, ItemStack.EMPTY)); 545 | enderMailman.setCarryingPackage(false); 546 | enderMailman.setDelivering(false); 547 | } else if ((enderMailman.tickCount - enderMailman.getTimePickedUp()) % 20 == 0) { 548 | enderMailman.teleportRandomly(); 549 | } 550 | } 551 | } 552 | 553 | static class TakePackageGoal extends Goal { 554 | private final EnderMailmanEntity enderMailman; 555 | 556 | public TakePackageGoal(EnderMailmanEntity enderMailman) { 557 | this.enderMailman = enderMailman; 558 | } 559 | 560 | @Override 561 | public boolean canUse() { 562 | return enderMailman.isDelivering() && !enderMailman.isCarryingPackage(); 563 | } 564 | 565 | @Override 566 | public void tick() { 567 | BlockEntity blockEntity = enderMailman.level().getBlockEntity(enderMailman.startingPos); 568 | if (blockEntity != null && blockEntity instanceof PackageBlockEntity) { 569 | PackageBlockEntity packageBlockEntity = (PackageBlockEntity) blockEntity; 570 | enderMailman.setContents(packageBlockEntity.getContents()); 571 | enderMailman.setCarryingPackage(true); 572 | enderMailman.level().setBlock(enderMailman.startingPos, Blocks.AIR.defaultBlockState(), 3); 573 | if (enderMailman.hasPackageController()) { 574 | enderMailman.getPackageControllerItem().setState(enderMailman.packageController, ControllerState.DELIVERING); 575 | } 576 | enderMailman.updateTimePickedUp(); 577 | } else { 578 | enderMailman.setDelivering(false); 579 | } 580 | } 581 | } 582 | 583 | static class DieGoal extends Goal { 584 | private final EnderMailmanEntity enderMailman; 585 | 586 | public DieGoal(EnderMailmanEntity enderMailman) { 587 | this.enderMailman = enderMailman; 588 | } 589 | 590 | @Override 591 | public boolean canUse() { 592 | return !enderMailman.isDelivering(); 593 | } 594 | 595 | @Override 596 | public void tick() { 597 | if (enderMailman.tickCount - enderMailman.getTimeDelivered() >= 100) { 598 | enderMailman.diePeacefully(); 599 | } 600 | } 601 | } 602 | 603 | } 604 | --------------------------------------------------------------------------------