From d3f591015e19cafd79eb3fe600dbb62478c382e9 Mon Sep 17 00:00:00 2001 From: candle Date: Sat, 10 May 2025 15:35:30 -0400 Subject: [PATCH] initial commit, should have parity with fabric --- .gitignore | 6 + README | 4 + build.gradle | 207 ++++++++++ gradle.properties | 49 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++ gradlew.bat | 92 +++++ settings.gradle | 15 + .../io/lampnet/travelerssuitcase/Config.java | 50 +++ .../SuitcaseRegistryState.java | 76 ++++ .../travelerssuitcase/TravelersSuitcase.java | 86 ++++ .../travelerssuitcase/Travelerssuitcase.java | 86 ++++ .../travelerssuitcase/block/ModBlocks.java | 88 +++++ .../block/PocketPortalBlock.java | 304 +++++++++++++++ .../block/SuitcaseBlock.java | 367 ++++++++++++++++++ .../block/entity/ModBlockEntities.java | 25 ++ .../block/entity/SuitcaseBlockEntity.java | 246 ++++++++++++ .../travelerssuitcase/item/KeystoneItem.java | 184 +++++++++ .../travelerssuitcase/item/ModItemGroups.java | 52 +++ .../travelerssuitcase/item/ModItems.java | 20 + src/main/resources/META-INF/mods.toml | 26 ++ .../blockstates/black_suitcase.json | 34 ++ .../blockstates/blue_suitcase.json | 34 ++ .../blockstates/cyan_suitcase.json | 34 ++ .../blockstates/gray_suitcase.json | 34 ++ .../blockstates/green_suitcase.json | 34 ++ .../blockstates/light_blue_suitcase.json | 34 ++ .../blockstates/light_gray_suitcase.json | 34 ++ .../blockstates/lime_suitcase.json | 34 ++ .../blockstates/magenta_suitcase.json | 34 ++ .../blockstates/orange_suitcase.json | 34 ++ .../blockstates/pink_suitcase.json | 34 ++ .../travelerssuitcase/blockstates/portal.json | 7 + .../blockstates/purple_suitcase.json | 34 ++ .../blockstates/red_suitcase.json | 34 ++ .../blockstates/suitcase.json | 34 ++ .../blockstates/white_suitcase.json | 34 ++ .../blockstates/yellow_suitcase.json | 34 ++ .../assets/travelerssuitcase/icon.png | Bin 0 -> 17015 bytes .../assets/travelerssuitcase/lang/en_us.json | 24 ++ .../models/block/black_suitcase_closed.json | 266 +++++++++++++ .../models/block/black_suitcase_open.json | 266 +++++++++++++ .../models/block/blue_suitcase_closed.json | 266 +++++++++++++ .../models/block/blue_suitcase_open.json | 266 +++++++++++++ .../models/block/cyan_suitcase_closed.json | 266 +++++++++++++ .../models/block/cyan_suitcase_open.json | 266 +++++++++++++ .../models/block/gray_suitcase_closed.json | 266 +++++++++++++ .../models/block/gray_suitcase_open.json | 266 +++++++++++++ .../models/block/green_suitcase_closed.json | 266 +++++++++++++ .../models/block/green_suitcase_open.json | 266 +++++++++++++ .../block/light_blue_suitcase_closed.json | 266 +++++++++++++ .../block/light_blue_suitcase_open.json | 266 +++++++++++++ .../block/light_gray_suitcase_closed.json | 266 +++++++++++++ .../block/light_gray_suitcase_open.json | 266 +++++++++++++ .../models/block/lime_suitcase_closed.json | 266 +++++++++++++ .../models/block/lime_suitcase_open.json | 266 +++++++++++++ .../models/block/magenta_suitcase_closed.json | 266 +++++++++++++ .../models/block/magenta_suitcase_open.json | 266 +++++++++++++ .../models/block/orange_suitcase_closed.json | 266 +++++++++++++ .../models/block/orange_suitcase_open.json | 266 +++++++++++++ .../models/block/pink_suitcase_closed.json | 266 +++++++++++++ .../models/block/pink_suitcase_open.json | 266 +++++++++++++ .../models/block/portal.json | 6 + .../models/block/purple_suitcase_closed.json | 266 +++++++++++++ .../models/block/purple_suitcase_open.json | 266 +++++++++++++ .../models/block/red_suitcase_closed.json | 266 +++++++++++++ .../models/block/red_suitcase_open.json | 266 +++++++++++++ .../models/block/suitcase_closed.json | 266 +++++++++++++ .../models/block/suitcase_open.json | 266 +++++++++++++ .../models/block/white_suitcase_closed.json | 266 +++++++++++++ .../models/block/white_suitcase_open.json | 266 +++++++++++++ .../models/block/yellow_suitcase_closed.json | 266 +++++++++++++ .../models/block/yellow_suitcase_open.json | 266 +++++++++++++ .../models/item/black_suitcase.json | 3 + .../models/item/blue_suitcase.json | 3 + .../models/item/cyan_suitcase.json | 3 + .../models/item/gray_suitcase.json | 3 + .../models/item/green_suitcase.json | 3 + .../models/item/keystone.json | 14 + .../models/item/keystone_unnamed.json | 6 + .../models/item/light_blue_suitcase.json | 3 + .../models/item/light_gray_suitcase.json | 3 + .../models/item/lime_suitcase.json | 3 + .../models/item/magenta_suitcase.json | 3 + .../models/item/orange_suitcase.json | 3 + .../models/item/pink_suitcase.json | 3 + .../travelerssuitcase/models/item/portal.json | 3 + .../models/item/purple_suitcase.json | 3 + .../models/item/red_suitcase.json | 3 + .../models/item/suitcase.json | 3 + .../models/item/white_suitcase.json | 3 + .../models/item/yellow_suitcase.json | 3 + .../textures/block/black_suitcase_closed.png | Bin 0 -> 3796 bytes .../textures/block/black_suitcase_open.png | Bin 0 -> 3952 bytes .../textures/block/blue_suitcase_closed.png | Bin 0 -> 4228 bytes .../textures/block/blue_suitcase_open.png | Bin 0 -> 4346 bytes .../textures/block/cyan_suitcase_closed.png | Bin 0 -> 4268 bytes .../textures/block/cyan_suitcase_open.png | Bin 0 -> 4377 bytes .../textures/block/gray_suitcase_closed.png | Bin 0 -> 3617 bytes .../textures/block/gray_suitcase_open.png | Bin 0 -> 3680 bytes .../textures/block/green_suitcase_closed.png | Bin 0 -> 4163 bytes .../textures/block/green_suitcase_open.png | Bin 0 -> 4324 bytes .../block/light_blue_suitcase_closed.png | Bin 0 -> 4063 bytes .../block/light_blue_suitcase_open.png | Bin 0 -> 4187 bytes .../block/light_gray_suitcase_closed.png | Bin 0 -> 3761 bytes .../block/light_gray_suitcase_open.png | Bin 0 -> 3811 bytes .../textures/block/lime_suitcase_closed.png | Bin 0 -> 4224 bytes .../textures/block/lime_suitcase_open.png | Bin 0 -> 4331 bytes .../block/magenta_suitcase_closed.png | Bin 0 -> 4301 bytes .../textures/block/magenta_suitcase_open.png | Bin 0 -> 4366 bytes .../textures/block/orange_suitcase_closed.png | Bin 0 -> 4175 bytes .../textures/block/orange_suitcase_open.png | Bin 0 -> 4289 bytes .../textures/block/pink_suitcase_closed.png | Bin 0 -> 4031 bytes .../textures/block/pink_suitcase_open.png | Bin 0 -> 4160 bytes .../textures/block/portal.png | Bin 0 -> 86 bytes .../textures/block/purple_suitcase_closed.png | Bin 0 -> 4250 bytes .../textures/block/purple_suitcase_open.png | Bin 0 -> 4389 bytes .../textures/block/red_suitcase_closed.png | Bin 0 -> 4065 bytes .../textures/block/red_suitcase_open.png | Bin 0 -> 4188 bytes .../textures/block/suitcase_closed.png | Bin 0 -> 2766 bytes .../textures/block/suitcase_open.png | Bin 0 -> 2819 bytes .../textures/block/white_suitcase_closed.png | Bin 0 -> 3891 bytes .../textures/block/white_suitcase_open.png | Bin 0 -> 3950 bytes .../textures/block/yellow_suitcase_closed.png | Bin 0 -> 4093 bytes .../textures/block/yellow_suitcase_open.png | Bin 0 -> 4233 bytes .../textures/item/keystone.png | Bin 0 -> 200 bytes .../textures/item/keystone_unnamed.png | Bin 0 -> 308 bytes .../dimension/pocket_dimension_.json | 16 + .../dimension/pocket_dimension_template.json | 16 + .../dimension_type/pocket_dimension_type.json | 20 + .../loot_tables/blocks/black_suitcase.json | 27 ++ .../loot_tables/blocks/blue_suitcase.json | 27 ++ .../loot_tables/blocks/cyan_suitcase.json | 27 ++ .../loot_tables/blocks/gray_suitcase.json | 27 ++ .../loot_tables/blocks/green_suitcase.json | 27 ++ .../blocks/light_blue_suitcase.json | 27 ++ .../blocks/light_gray_suitcase.json | 27 ++ .../loot_tables/blocks/lime_suitcase.json | 27 ++ .../loot_tables/blocks/magenta_suitcase.json | 27 ++ .../loot_tables/blocks/orange_suitcase.json | 27 ++ .../loot_tables/blocks/pink_suitcase.json | 27 ++ .../loot_tables/blocks/purple_suitcase.json | 27 ++ .../loot_tables/blocks/red_suitcase.json | 27 ++ .../loot_tables/blocks/suitcase.json | 27 ++ .../loot_tables/blocks/white_suitcase.json | 27 ++ .../loot_tables/blocks/yellow_suitcase.json | 27 ++ .../recipes/black_suitcase.json | 15 + .../recipes/blue_suitcase.json | 15 + .../recipes/brown_suitcase.json | 15 + .../recipes/cyan_suitcase.json | 15 + .../recipes/gray_suitcase.json | 15 + .../recipes/green_suitcase.json | 15 + .../travelerssuitcase/recipes/keystone.json | 23 ++ .../recipes/light_blue_suitcase.json | 15 + .../recipes/light_gray_suitcase.json | 15 + .../recipes/lime_suitcase.json | 15 + .../recipes/magenta_suitcase.json | 15 + .../recipes/orange_suitcase.json | 15 + .../recipes/pink_suitcase.json | 15 + .../recipes/purple_suitcase.json | 15 + .../recipes/red_suitcase.json | 15 + .../travelerssuitcase/recipes/suitcase.json | 23 ++ .../recipes/white_suitcase.json | 15 + .../recipes/yellow_suitcase.json | 15 + .../structures/pocket_island_01.nbt | Bin 0 -> 317371 bytes .../tags/items/suitcases.json | 21 + .../worldgen/biome/pocket_islands.json | 54 +++ .../worldgen/dimension/pocket_dimension_.json | 28 ++ src/main/resources/pack.mcmeta | 6 + .../resources/travelerssuitcase.mixins.json | 14 + 171 files changed, 12296 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/io/lampnet/travelerssuitcase/Config.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/SuitcaseRegistryState.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/TravelersSuitcase.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/Travelerssuitcase.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/block/ModBlocks.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/block/PocketPortalBlock.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/block/SuitcaseBlock.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/block/entity/ModBlockEntities.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/block/entity/SuitcaseBlockEntity.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/item/KeystoneItem.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/item/ModItemGroups.java create mode 100644 src/main/java/io/lampnet/travelerssuitcase/item/ModItems.java create mode 100644 src/main/resources/META-INF/mods.toml create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/black_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/blue_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/cyan_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/gray_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/green_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/light_blue_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/light_gray_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/lime_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/magenta_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/orange_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/pink_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/portal.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/purple_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/red_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/white_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/blockstates/yellow_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/icon.png create mode 100644 src/main/resources/assets/travelerssuitcase/lang/en_us.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/black_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/black_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/blue_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/blue_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/cyan_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/cyan_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/gray_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/gray_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/green_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/green_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/light_blue_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/light_blue_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/light_gray_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/light_gray_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/lime_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/lime_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/magenta_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/magenta_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/orange_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/orange_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/pink_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/pink_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/portal.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/purple_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/purple_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/red_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/red_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/white_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/white_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/yellow_suitcase_closed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/block/yellow_suitcase_open.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/black_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/blue_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/cyan_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/gray_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/green_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/keystone.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/keystone_unnamed.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/light_blue_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/light_gray_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/lime_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/magenta_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/orange_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/pink_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/portal.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/purple_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/red_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/white_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/models/item/yellow_suitcase.json create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/black_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/black_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/blue_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/blue_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/cyan_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/cyan_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/gray_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/gray_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/green_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/green_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/light_blue_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/light_blue_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/light_gray_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/light_gray_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/lime_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/lime_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/magenta_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/magenta_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/orange_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/orange_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/pink_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/pink_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/portal.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/purple_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/purple_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/red_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/red_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/white_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/white_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/yellow_suitcase_closed.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/block/yellow_suitcase_open.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/item/keystone.png create mode 100644 src/main/resources/assets/travelerssuitcase/textures/item/keystone_unnamed.png create mode 100644 src/main/resources/data/travelerssuitcase/dimension/pocket_dimension_.json create mode 100644 src/main/resources/data/travelerssuitcase/dimension/pocket_dimension_template.json create mode 100644 src/main/resources/data/travelerssuitcase/dimension_type/pocket_dimension_type.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/black_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/blue_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/cyan_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/gray_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/green_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/light_blue_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/light_gray_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/lime_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/magenta_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/orange_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/pink_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/purple_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/red_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/white_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/loot_tables/blocks/yellow_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/black_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/blue_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/brown_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/cyan_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/gray_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/green_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/keystone.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/light_blue_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/light_gray_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/lime_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/magenta_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/orange_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/pink_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/purple_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/red_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/white_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/recipes/yellow_suitcase.json create mode 100644 src/main/resources/data/travelerssuitcase/structures/pocket_island_01.nbt create mode 100644 src/main/resources/data/travelerssuitcase/tags/items/suitcases.json create mode 100644 src/main/resources/data/travelerssuitcase/worldgen/biome/pocket_islands.json create mode 100644 src/main/resources/data/travelerssuitcase/worldgen/dimension/pocket_dimension_.json create mode 100644 src/main/resources/pack.mcmeta create mode 100644 src/main/resources/travelerssuitcase.mixins.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d220c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +bin/ +run/ +run-data/ +.idea/ +.gradle/ diff --git a/README b/README new file mode 100644 index 0000000..e622bff --- /dev/null +++ b/README @@ -0,0 +1,4 @@ +Traveler's Suitcase [FORGE] +ORIGINAL: https://github.com/BennyBoops/PocketRepose + +This is a forge port of the PocketRepose mod. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..84c3895 --- /dev/null +++ b/build.gradle @@ -0,0 +1,207 @@ +buildscript { + repositories { + // These repositories are only for Gradle plugins, put any other repositories in the repository block further below + maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } + mavenCentral() + } + dependencies { + classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT' + } +} + +plugins { + id 'eclipse' + id 'idea' + id 'net.minecraftforge.gradle' version '[6.0.16,6.2)' +} + +apply plugin: 'org.spongepowered.mixin' + +group = mod_group_id +version = mod_version + +base { + archivesName = mod_id +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(17) +} + +minecraft { + // The mappings can be changed at any time and must be in the following format. + // Channel: Version: + // official MCVersion Official field/method names from Mojang mapping files + // parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official + // + // You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. + // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md + // + // Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge + // Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started + // + // Use non-default mappings at your own risk. They may not always work. + // Simply re-run your setup task after changing the mappings to update your workspace. + mappings channel: mapping_channel, version: mapping_version + + // When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game. + // In most cases, it is not necessary to enable. + // enableEclipsePrepareRuns = true + // enableIdeaPrepareRuns = true + + // This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game. + // It is REQUIRED to be set to true for this template to function. + // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html + copyIdeResources = true + + // When true, this property will add the folder name of all declared run configurations to generated IDE run configurations. + // The folder name can be set on a run configuration using the "folderName" property. + // By default, the folder name of a run configuration is the name of the Gradle project containing it. + // generateRunFolders = true + + // This property enables access transformers for use in development. + // They will be applied to the Minecraft artifact. + // The access transformer file can be anywhere in the project. + // However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge. + // This default location is a best practice to automatically put the file in the right place in the final jar. + // See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information. + // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + // applies to all the run configs below + configureEach { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + mods { + "${mod_id}" { + source sourceSets.main + } + } + } + + client { + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + property 'forge.enabledGameTestNamespaces', mod_id + } + + server { + property 'forge.enabledGameTestNamespaces', mod_id + args '--nogui' + } + + // This run config launches GameTestServer and runs all registered gametests, then exits. + // By default, the server will crash when no gametests are provided. + // The gametest system is also enabled by default for other run configs under the /test command. + gameTestServer { + property 'forge.enabledGameTestNamespaces', mod_id + } + + data { + // example of overriding the workingDirectory set in configureEach above + workingDirectory project.file('run-data') + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + } + } +} + +mixin { + add sourceSets.main, "${mod_id}.refmap.json" + + config "${mod_id}.mixins.json" +} + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + +repositories { + // Put repositories for dependencies here + // ForgeGradle automatically adds the Forge maven and Maven Central for you + + // If you have mod jar dependencies in ./libs, you can declare them as a repository like so. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver + // flatDir { + // dir 'libs' + // } +} + +dependencies { + // Specify the version of Minecraft to use. + // Any artifact can be supplied so long as it has a "userdev" classifier artifact and is a compatible patcher artifact. + // The "userdev" classifier will be requested and setup by ForgeGradle. + // If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"], + // then special handling is done to allow a setup of a vanilla dependency without the use of an external repository. + minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" + + // Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings + // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime + // compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}") + // compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}") + // runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}") + + // Example mod dependency using a mod jar from ./libs with a flat dir repository + // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar + // The group id is ignored when searching -- in this case, it is "blank" + // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") + + // For more info: + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html + + annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' + +} + +// This block of code expands all declared replace properties in the specified resource targets. +// A missing property will result in an error. Properties are expanded using ${} Groovy notation. +// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. +// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html +tasks.named('processResources', ProcessResources).configure { + var replaceProperties = [minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range, + forge_version : forge_version, forge_version_range: forge_version_range, + loader_version_range: loader_version_range, + mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, + mod_authors : mod_authors, mod_description: mod_description,] + + inputs.properties replaceProperties + + filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) { + expand replaceProperties + [project: project] + } +} + +// Example for how to get properties into the manifest for reading at runtime. +tasks.named('jar', Jar).configure { + manifest { + attributes(["Specification-Title" : mod_id, + "Specification-Vendor" : mod_authors, + "Specification-Version" : "1", // We are version 1 of ourselves + "Implementation-Title" : project.name, + "Implementation-Version" : project.jar.archiveVersion, + "Implementation-Vendor" : mod_authors, + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")]) + } + + // This is the preferred method to reobfuscate your jar file + finalizedBy 'reobfJar' +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..d011161 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,49 @@ +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false +# The Minecraft version must agree with the Forge version to get a valid artifact +minecraft_version=1.20.1 +# The Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft_version_range=[1.20.1,1.21) +# The Forge version must agree with the Minecraft version to get a valid artifact +forge_version=47.4.0 +# The Forge version range can use any version of Forge as bounds or match the loader version range +forge_version_range=[47,) +# The loader version range can only use the major version of Forge/FML as bounds +loader_version_range=[47,) +# The mapping channel to use for mappings. +# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"]. +# Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin. +# +# | Channel | Version | | +# |-----------|----------------------|--------------------------------------------------------------------------------| +# | official | MCVersion | Official field/method names from Mojang mapping files | +# | parchment | YYYY.MM.DD-MCVersion | Open community-sourced parameter names and javadocs layered on top of official | +# +# You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. +# See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md +# +# Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge. +# Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started +mapping_channel=official +# The mapping version to query from the mapping channel. +# This must match the format required by the mapping channel. +mapping_version=1.20.1 +# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} +# Must match the String constant located in the main mod class annotated with @Mod. +mod_id=travelerssuitcase +# The human-readable display name for the mod. +mod_name=TravelersSuitcase +# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. +mod_license=MIT +# The mod version. See https://semver.org/ +mod_version=1.0-SNAPSHOT +# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. +# This should match the base package used for the mod sources. +# See https://maven.apache.org/guides/mini/guide-naming-conventions.html +mod_group_id=io.lampnet +# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. +mod_authors=BennyBoops, Candle +# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. +mod_description=Forge Port of Pocket Repose diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..7101f8e --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..b46bbb4 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,15 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { + name = 'MinecraftForge' + url = 'https://maven.minecraftforge.net/' + } + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' +} + +rootProject.name = 'travelerssuitcase' diff --git a/src/main/java/io/lampnet/travelerssuitcase/Config.java b/src/main/java/io/lampnet/travelerssuitcase/Config.java new file mode 100644 index 0000000..0490065 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/Config.java @@ -0,0 +1,50 @@ +package io.lampnet.travelerssuitcase; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.config.ModConfigEvent; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +// An example config class. This is not required, but it's a good idea to have one to keep your config organized. +// Demonstrates how to use Forge's config APIs +@Mod.EventBusSubscriber(modid = TravelersSuitcase.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class Config { + private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder(); + + private static final ForgeConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER.comment("Whether to log the dirt block on common setup").define("logDirtBlock", true); + + private static final ForgeConfigSpec.IntValue MAGIC_NUMBER = BUILDER.comment("A magic number").defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); + + public static final ForgeConfigSpec.ConfigValue MAGIC_NUMBER_INTRODUCTION = BUILDER.comment("What you want the introduction message to be for the magic number").define("magicNumberIntroduction", "The magic number is... "); + + // a list of strings that are treated as resource locations for items + private static final ForgeConfigSpec.ConfigValue> ITEM_STRINGS = BUILDER.comment("A list of items to log on common setup.").defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName); + + static final ForgeConfigSpec SPEC = BUILDER.build(); + + public static boolean logDirtBlock; + public static int magicNumber; + public static String magicNumberIntroduction; + public static Set items; + + private static boolean validateItemName(final Object obj) { + return obj instanceof final String itemName && ForgeRegistries.ITEMS.containsKey(new ResourceLocation(itemName)); + } + + @SubscribeEvent + static void onLoad(final ModConfigEvent event) { + logDirtBlock = LOG_DIRT_BLOCK.get(); + magicNumber = MAGIC_NUMBER.get(); + magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get(); + + // convert the list of strings into a set of items + items = ITEM_STRINGS.get().stream().map(itemName -> ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemName))).collect(Collectors.toSet()); + } +} diff --git a/src/main/java/io/lampnet/travelerssuitcase/SuitcaseRegistryState.java b/src/main/java/io/lampnet/travelerssuitcase/SuitcaseRegistryState.java new file mode 100644 index 0000000..243d719 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/SuitcaseRegistryState.java @@ -0,0 +1,76 @@ +package io.lampnet.travelerssuitcase; + +import io.lampnet.travelerssuitcase.block.entity.SuitcaseBlockEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; + +import java.util.HashMap; +import java.util.Map; + +public class SuitcaseRegistryState extends SavedData { + private static final String REGISTRY_KEY = "travelers_suitcase_registry"; + private final Map> registry = new HashMap<>(); + + public SuitcaseRegistryState() { + super(); + } + + @Override + public CompoundTag save(CompoundTag nbt) { + ListTag keystonesList = new ListTag(); + for (Map.Entry> keystoneEntry : registry.entrySet()) { + CompoundTag keystoneNbt = new CompoundTag(); + keystoneNbt.putString("KeystoneName", keystoneEntry.getKey()); + ListTag playersList = new ListTag(); + for (Map.Entry playerEntry : keystoneEntry.getValue().entrySet()) { + CompoundTag playerNbt = new CompoundTag(); + playerNbt.putString("UUID", playerEntry.getKey()); + BlockPos pos = playerEntry.getValue(); + playerNbt.putInt("X", pos.getX()); + playerNbt.putInt("Y", pos.getY()); + playerNbt.putInt("Z", pos.getZ()); + + playersList.add(playerNbt); + } + keystoneNbt.put("Players", playersList); + keystonesList.add(keystoneNbt); + } + nbt.put("SuitcaseRegistry", keystonesList); + return nbt; + } + + public static SuitcaseRegistryState load(CompoundTag nbt) { + SuitcaseRegistryState state = new SuitcaseRegistryState(); + if (nbt.contains("SuitcaseRegistry")) { + ListTag keystonesList = nbt.getList("SuitcaseRegistry", Tag.TAG_COMPOUND); + for (int i = 0; i < keystonesList.size(); i++) { + CompoundTag keystoneNbt = keystonesList.getCompound(i); + String keystoneName = keystoneNbt.getString("KeystoneName"); + Map playersMap = new HashMap<>(); + ListTag playersList = keystoneNbt.getList("Players", Tag.TAG_COMPOUND); + for (int j = 0; j < playersList.size(); j++) { + CompoundTag playerNbt = playersList.getCompound(j); + String uuid = playerNbt.getString("UUID"); + BlockPos pos = new BlockPos( + playerNbt.getInt("X"), + playerNbt.getInt("Y"), + playerNbt.getInt("Z") + ); + playersMap.put(uuid, pos); + } + state.registry.put(keystoneName, playersMap); + } + } + return state; + } + + public static SuitcaseRegistryState getState(MinecraftServer server) { + return server.getLevel(Level.OVERWORLD).getDataStorage() + .computeIfAbsent(SuitcaseRegistryState::load, SuitcaseRegistryState::new, REGISTRY_KEY); + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/TravelersSuitcase.java b/src/main/java/io/lampnet/travelerssuitcase/TravelersSuitcase.java new file mode 100644 index 0000000..390d473 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/TravelersSuitcase.java @@ -0,0 +1,86 @@ +package io.lampnet.travelerssuitcase; + +import io.lampnet.travelerssuitcase.block.ModBlocks; +import io.lampnet.travelerssuitcase.block.entity.ModBlockEntities; +// import io.lampnet.travelerssuitcase.block.entity.SuitcaseBlockEntity; // Commented out for now +// import io.lampnet.travelerssuitcase.config.SuitcaseRegistryState; // Commented out for now +import io.lampnet.travelerssuitcase.item.ModItemGroups; +import io.lampnet.travelerssuitcase.item.ModItems; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Mod(TravelersSuitcase.MODID) +public class TravelersSuitcase { + public static final String MODID = "travelerssuitcase"; + public static final Logger LOGGER = LogManager.getLogger(MODID); + + public TravelersSuitcase() { + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + + // Register items, blocks, etc. + ModItems.register(modEventBus); + ModBlocks.register(modEventBus); + ModItemGroups.register(modEventBus); + ModBlockEntities.register(modEventBus); + + // Register world load event + MinecraftForge.EVENT_BUS.addListener(this::onWorldLoad); + + // Register server events + // MinecraftForge.EVENT_BUS.addListener(this::onServerStarting); + // MinecraftForge.EVENT_BUS.addListener(this::onServerStopping); + + LOGGER.info("Initializing " + MODID); + } + + private void onWorldLoad(net.minecraftforge.event.level.LevelEvent.Load event) { + if (event.getLevel() instanceof net.minecraft.server.level.ServerLevel world) { + if (world.dimension().location().getNamespace().equals(MODID)) { + String dimensionName = world.dimension().location().getPath(); + java.nio.file.Path structureMarkerPath = world.getServer().getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT) + .resolve("data") + .resolve(MODID) + .resolve("pending_structures") + .resolve(dimensionName + ".txt"); + if (java.nio.file.Files.exists(structureMarkerPath)) { + try { + net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate template = world.getServer().getStructureManager() + .get(new net.minecraft.resources.ResourceLocation(MODID, "pocket_island_01")) + .orElse(null); + if (template != null) { + net.minecraft.core.BlockPos pos = new net.minecraft.core.BlockPos(0, 64, 0); + template.placeInWorld( + world, + pos, + pos, + new net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings(), + world.getRandom(), + net.minecraft.world.level.block.Block.UPDATE_ALL + ); + java.nio.file.Files.delete(structureMarkerPath); + } + } catch (java.io.IOException e) { + LOGGER.error("Failed to place structure", e); + } + } + } + } + } + + /* Commented out for now + private void onServerStarting(net.minecraftforge.event.server.ServerStartingEvent event) { + SuitcaseRegistryState state = SuitcaseRegistryState.getState(event.getServer()); + SuitcaseBlockEntity.initializeSuitcaseRegistry(state.registry); + } + + private void onServerStopping(net.minecraftforge.event.server.ServerStoppingEvent event) { + SuitcaseRegistryState state = SuitcaseRegistryState.getState(event.getServer()); + SuitcaseBlockEntity.saveSuitcaseRegistryTo(state.registry); + state.setDirty(); + } + */ +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/Travelerssuitcase.java b/src/main/java/io/lampnet/travelerssuitcase/Travelerssuitcase.java new file mode 100644 index 0000000..8c8fad5 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/Travelerssuitcase.java @@ -0,0 +1,86 @@ +package io.lampnet.travelerssuitcase; + +import io.lampnet.travelerssuitcase.block.ModBlocks; +import io.lampnet.travelerssuitcase.block.entity.ModBlockEntities; +// import io.lampnet.travelerssuitcase.block.entity.SuitcaseBlockEntity; // Commented out for now +// import io.lampnet.travelerssuitcase.config.SuitcaseRegistryState; // Commented out for now +import io.lampnet.travelerssuitcase.item.ModItemGroups; +import io.lampnet.travelerssuitcase.item.ModItems; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Mod(TravelersSuitcase.MODID) +public class Travelerssuitcase { + public static final String MODID = "travelerssuitcase"; + public static final Logger LOGGER = LogManager.getLogger(MODID); + + public Travelerssuitcase() { + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + + // Register items, blocks, etc. + ModItems.register(modEventBus); + ModBlocks.register(modEventBus); + ModItemGroups.register(modEventBus); + ModBlockEntities.register(modEventBus); + + // Register world load event + MinecraftForge.EVENT_BUS.addListener(this::onWorldLoad); + + // Register server events + // MinecraftForge.EVENT_BUS.addListener(this::onServerStarting); + // MinecraftForge.EVENT_BUS.addListener(this::onServerStopping); + + LOGGER.info("Initializing " + MODID); + } + + private void onWorldLoad(net.minecraftforge.event.level.LevelEvent.Load event) { + if (event.getLevel() instanceof net.minecraft.server.level.ServerLevel world) { + if (world.dimension().location().getNamespace().equals(MODID)) { + String dimensionName = world.dimension().location().getPath(); + java.nio.file.Path structureMarkerPath = world.getServer().getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT) + .resolve("data") + .resolve(MODID) + .resolve("pending_structures") + .resolve(dimensionName + ".txt"); + if (java.nio.file.Files.exists(structureMarkerPath)) { + try { + net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate template = world.getServer().getStructureManager() + .get(new net.minecraft.resources.ResourceLocation(MODID, "pocket_island_01")) + .orElse(null); + if (template != null) { + net.minecraft.core.BlockPos pos = new net.minecraft.core.BlockPos(0, 64, 0); + template.placeInWorld( + world, + pos, + pos, + new net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings(), + world.getRandom(), + net.minecraft.world.level.block.Block.UPDATE_ALL + ); + java.nio.file.Files.delete(structureMarkerPath); + } + } catch (java.io.IOException e) { + LOGGER.error("Failed to place structure", e); + } + } + } + } + } + + /* Commented out for now + private void onServerStarting(net.minecraftforge.event.server.ServerStartingEvent event) { + SuitcaseRegistryState state = SuitcaseRegistryState.getState(event.getServer()); + SuitcaseBlockEntity.initializeSuitcaseRegistry(state.registry); + } + + private void onServerStopping(net.minecraftforge.event.server.ServerStoppingEvent event) { + SuitcaseRegistryState state = SuitcaseRegistryState.getState(event.getServer()); + SuitcaseBlockEntity.saveSuitcaseRegistryTo(state.registry); + state.setDirty(); + } + */ +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/block/ModBlocks.java b/src/main/java/io/lampnet/travelerssuitcase/block/ModBlocks.java new file mode 100644 index 0000000..6494fb2 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/block/ModBlocks.java @@ -0,0 +1,88 @@ +package io.lampnet.travelerssuitcase.block; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import io.lampnet.travelerssuitcase.item.ModItems; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import net.minecraft.world.level.block.state.BlockState; + +public class ModBlocks { + public static final DeferredRegister BLOCKS = + DeferredRegister.create(ForgeRegistries.BLOCKS, TravelersSuitcase.MODID); + + private static ToIntFunction getLuminance(boolean openCheck) { + if (openCheck) { + return (state) -> state.getValue(SuitcaseBlock.OPEN) ? 8 : 0; + } + return (state) -> 0; + } + private static ToIntFunction getPortalLuminance() { + return (state) -> 10; + } + + public static final RegistryObject SUITCASE = registerBlock("suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.BROWN_WOOL) + .sound(SoundType.WOOL) + .strength(0.2f) + .noOcclusion() + .lightLevel(getLuminance(true)))); + public static final RegistryObject WHITE_SUITCASE = registerBlock("white_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.WHITE_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject BLACK_SUITCASE = registerBlock("black_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.BLACK_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject LIGHT_GRAY_SUITCASE = registerBlock("light_gray_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.LIGHT_GRAY_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject GRAY_SUITCASE = registerBlock("gray_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.GRAY_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject RED_SUITCASE = registerBlock("red_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.RED_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject ORANGE_SUITCASE = registerBlock("orange_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.ORANGE_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject YELLOW_SUITCASE = registerBlock("yellow_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.YELLOW_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject LIME_SUITCASE = registerBlock("lime_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.LIME_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject GREEN_SUITCASE = registerBlock("green_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.GREEN_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject CYAN_SUITCASE = registerBlock("cyan_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.CYAN_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject LIGHT_BLUE_SUITCASE = registerBlock("light_blue_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.LIGHT_BLUE_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject BLUE_SUITCASE = registerBlock("blue_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.BLUE_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject PURPLE_SUITCASE = registerBlock("purple_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.PURPLE_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject MAGENTA_SUITCASE = registerBlock("magenta_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.MAGENTA_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + public static final RegistryObject PINK_SUITCASE = registerBlock("pink_suitcase", + () -> new SuitcaseBlock(BlockBehaviour.Properties.copy(Blocks.PINK_WOOL).sound(SoundType.WOOL).strength(0.2f).noOcclusion().lightLevel(getLuminance(true)))); + + public static final RegistryObject PORTAL = registerBlock("portal", + () -> new PocketPortalBlock(BlockBehaviour.Properties.copy(Blocks.NETHER_PORTAL) + .sound(SoundType.LODESTONE) + .noOcclusion() + .lightLevel(getPortalLuminance()) + .strength(-1f))); + + private static RegistryObject registerBlock(String name, Supplier blockSupplier) { + RegistryObject block = BLOCKS.register(name, blockSupplier); + ModItems.ITEMS.register(name, () -> new BlockItem(block.get(), new Item.Properties())); + return block; + } + + public static void register(IEventBus eventBus) { + BLOCKS.register(eventBus); + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/block/PocketPortalBlock.java b/src/main/java/io/lampnet/travelerssuitcase/block/PocketPortalBlock.java new file mode 100644 index 0000000..76d59ab --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/block/PocketPortalBlock.java @@ -0,0 +1,304 @@ +package io.lampnet.travelerssuitcase.block; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import io.lampnet.travelerssuitcase.block.entity.SuitcaseBlockEntity; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.network.chat.Component; +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.level.Level; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.nbt.Tag; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PocketPortalBlock extends Block { + private static final Map LAST_KNOWN_POSITIONS = new HashMap<>(); + private static final int SEARCH_RADIUS_CHUNKS = 12; + public static class PlayerPositionData { + public final double x; + public final double y; + public final double z; + public final float yaw; + public final float pitch; + public final long timestamp; + public PlayerPositionData(double x, double y, double z, float yaw, float pitch) { + this.x = x; + this.y = y; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + this.timestamp = System.currentTimeMillis(); + } + } + + public PocketPortalBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + public static void storePlayerPosition(ServerPlayer player) { + LAST_KNOWN_POSITIONS.put( + player.getUUID().toString(), + new PlayerPositionData(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()) + ); + } + + private boolean attemptPlayerInventorySuitcaseTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) { + for (ServerPlayer serverPlayer : world.getServer().getPlayerList().getPlayers()) { + if (scanPlayerInventoryForSuitcase(serverPlayer, player, keystoneName, world, overworld)) { + return true; + } + } + return false; + } + + private boolean scanPlayerInventoryForSuitcase(ServerPlayer inventoryOwner, ServerPlayer exitingPlayer, + String keystoneName, Level world, ServerLevel overworld) { + for (int i = 0; i < inventoryOwner.getInventory().getContainerSize(); i++) { + ItemStack stack = inventoryOwner.getInventory().getItem(i); + if (isSuitcaseItemWithKeystone(stack, keystoneName)) { + cleanUpSuitcaseItemNbt(stack, exitingPlayer, keystoneName); + inventoryOwner.getInventory().setItem(i, stack); + teleportToPosition(world, exitingPlayer, overworld, + inventoryOwner.getX(), inventoryOwner.getY() + 1.0, inventoryOwner.getZ(), + exitingPlayer.getYRot(), exitingPlayer.getXRot()); + + return true; + } + } + return false; + } + + private boolean isSuitcaseItemWithKeystone(ItemStack stack, String keystoneName) { + if (stack.isEmpty() || !(stack.getItem() instanceof BlockItem) || + !(((BlockItem) stack.getItem()).getBlock() instanceof SuitcaseBlock)) { + return false; + } + + if (!stack.hasTag()) return false; + CompoundTag beTag = BlockItem.getBlockEntityData(stack); + if (beTag == null) return false; + + return beTag.contains("BoundKeystone") && keystoneName.equals(beTag.getString("BoundKeystone")); + } + + private void cleanUpSuitcaseItemNbt(ItemStack stack, ServerPlayer player, String keystoneName) { + if (!stack.hasTag()) return; + CompoundTag beTag = BlockItem.getBlockEntityData(stack); + if (beTag == null) return; + if (beTag.contains("EnteredPlayers", Tag.TAG_LIST)) { + ListTag playersList = beTag.getList("EnteredPlayers", Tag.TAG_COMPOUND); + ListTag newPlayersList = new ListTag(); + boolean playerFound = false; + for (int i = 0; i < playersList.size(); i++) { + CompoundTag playerData = playersList.getCompound(i); + if (!player.getUUID().toString().equals(playerData.getString("UUID"))) { + newPlayersList.add(playerData); + } else { + playerFound = true; + } + } + if (playerFound) { + beTag.put("EnteredPlayers", newPlayersList); + int remainingPlayers = newPlayersList.size(); + updateItemLore(stack, remainingPlayers); + } + } + SuitcaseBlockEntity.removeSuitcaseEntry(keystoneName, player.getUUID().toString()); + } + + @Override + public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { + if (!world.isClientSide && entity instanceof ServerPlayer player) { + ResourceLocation dimensionLocation = world.dimension().location(); + String currentDimensionPath = dimensionLocation.getPath(); + if (currentDimensionPath.startsWith("pocket_dimension_")) { + String keystoneName = currentDimensionPath.replace("pocket_dimension_", ""); + preparePlayerForTeleport(player); + world.playSound(null, pos, SoundEvents.BUNDLE_DROP_CONTENTS, SoundSource.PLAYERS, 2.0f, 1.0f); + ServerLevel overworld = world.getServer().getLevel(Level.OVERWORLD); + if (overworld == null) return; + + boolean teleported = false; + + // Method 1: Try to teleport to the original suitcase block entity + teleported = attemptSuitcaseTeleport(world, overworld, player, keystoneName); + + // Method 2: Try to find suitcase in a player's inventory (new method) + if (!teleported) { + teleported = attemptPlayerInventorySuitcaseTeleport(world, overworld, player, keystoneName); + } + + // Method 3: Try to find the suitcase as an item entity in the world + if (!teleported) { + teleported = attemptSuitcaseItemTeleport(world, overworld, player, keystoneName); + } + + // Method 4: Try to use player's last known position + if (!teleported) { + teleported = attemptLastKnownPositionTeleport(world, overworld, player); + } + + // Fallback: Take them to spawn + if (!teleported) { + player.displayClientMessage(Component.literal("§cCouldn't find your return point. Taking you to spawn.").withStyle(ChatFormatting.RED), false); + teleportToPosition(world, player, overworld, + overworld.getSharedSpawnPos().getX() + 0.5, + overworld.getSharedSpawnPos().getY() + 1.0, + overworld.getSharedSpawnPos().getZ() + 0.5, 0, 0); + } + SuitcaseBlockEntity.removeSuitcaseEntry(keystoneName, player.getUUID().toString()); + LAST_KNOWN_POSITIONS.remove(player.getUUID().toString()); + } + } + } + + private void preparePlayerForTeleport(ServerPlayer player) { + player.stopRiding(); + player.setDeltaMovement(Vec3.ZERO); + player.fallDistance = 0f; + } + + // Method 1: Try to teleport to the original suitcase block entity + private boolean attemptSuitcaseTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) { + BlockPos suitcasePos = SuitcaseBlockEntity.findSuitcasePosition(keystoneName, player.getUUID().toString()); + if (suitcasePos == null) return false; + ChunkPos suitcaseChunkPos = new ChunkPos(suitcasePos); + overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, true); + try { + BlockEntity targetEntity = overworld.getBlockEntity(suitcasePos); + if (targetEntity instanceof SuitcaseBlockEntity suitcase) { + SuitcaseBlockEntity.EnteredPlayerData exitData = suitcase.getExitPosition(player.getUUID().toString()); + if (exitData != null) { + teleportToPosition(world, player, overworld, exitData.x, exitData.y, exitData.z, exitData.yaw, player.getXRot()); + world.getServer().execute(() -> { + overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, false); + }); + return true; + } + } + } finally { + overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, false); + } + return false; + } + + // Method 2: Try to find the suitcase as an item entity in the world + private boolean attemptSuitcaseItemTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) { + BlockPos searchCenter = null; + BlockPos suitcasePos = SuitcaseBlockEntity.findSuitcasePosition(keystoneName, player.getUUID().toString()); + if (suitcasePos != null) { + searchCenter = suitcasePos; + } else { + PlayerPositionData lastPos = LAST_KNOWN_POSITIONS.get(player.getUUID().toString()); + if (lastPos != null) { + searchCenter = new BlockPos((int)lastPos.x, (int)lastPos.y, (int)lastPos.z); + } else { + searchCenter = overworld.getSharedSpawnPos(); + } + } + int centerX = searchCenter.getX() >> 4; + int centerZ = searchCenter.getZ() >> 4; + for (int radius = 0; radius <= SEARCH_RADIUS_CHUNKS; radius++) { + for (int x = centerX - radius; x <= centerX + radius; x++) { + for (int z = centerZ - radius; z <= centerZ + radius; z++) { + if (radius > 0 && x > centerX - radius && x < centerX + radius && + z > centerZ - radius && z < centerZ + radius) { + continue; + } + if (!overworld.hasChunk(x, z)) { + continue; + } + LevelChunk chunk = overworld.getChunk(x, z); + List itemEntities = overworld.getEntitiesOfClass( + ItemEntity.class, + new AABB(chunk.getPos().getMinBlockX(), overworld.getMinBuildHeight(), chunk.getPos().getMinBlockZ(), + chunk.getPos().getMaxBlockX(), overworld.getMaxBuildHeight(), chunk.getPos().getMaxBlockZ()), + itemEntity -> { + ItemStack stack = itemEntity.getItem(); + CompoundTag beTag = BlockItem.getBlockEntityData(stack); + if (beTag == null) return false; + + return beTag.contains("BoundKeystone") && + keystoneName.equals(beTag.getString("BoundKeystone")); + } + ); + if (!itemEntities.isEmpty()) { + ItemEntity suitcaseItemEntity = itemEntities.get(0); + cleanUpSuitcaseItemNbt(suitcaseItemEntity.getItem(), player, keystoneName); + suitcaseItemEntity.setItem(suitcaseItemEntity.getItem()); + teleportToPosition(world, player, overworld, + suitcaseItemEntity.getX(), suitcaseItemEntity.getY() + 1.0, suitcaseItemEntity.getZ(), + player.getYRot(), player.getXRot()); + return true; + } + } + } + } + return false; + } + + // Method 3: Try to use player's last known position + private boolean attemptLastKnownPositionTeleport(Level world, ServerLevel overworld, ServerPlayer player) { + PlayerPositionData lastPos = LAST_KNOWN_POSITIONS.get(player.getUUID().toString()); + if (lastPos != null) { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastPos.timestamp > 10 * 60 * 1000) { + return false; + } + player.displayClientMessage(Component.literal("§6Returning to your last known position."), false); + teleportToPosition(world, player, overworld, + lastPos.x, lastPos.y, lastPos.z, + lastPos.yaw, lastPos.pitch); + return true; + } + return false; + } + + private void teleportToPosition(Level world, ServerPlayer player, ServerLevel targetWorld, + double x, double y, double z, float yaw, float pitch) { + player.teleportTo(targetWorld, x, y, z, yaw, pitch); + } + + private void updateItemLore(ItemStack stack, int playerCount) { + if (stack.hasTag() && stack.getTag().contains("display")) { + CompoundTag display = stack.getTag().getCompound("display"); + if (display != null && display.contains("Lore", Tag.TAG_LIST)) { + ListTag lore = display.getList("Lore", Tag.TAG_STRING); + ListTag newLore = new ListTag(); + for (int i = 0; i < lore.size(); i++) { + String loreStr = lore.getString(i); + if (!loreStr.contains("traveler")) { + newLore.add(lore.get(i)); + } + } + if (playerCount > 0) { + Component warningText = Component.literal("§c⚠ Contains " + playerCount + " traveler(s)!") + .withStyle(ChatFormatting.RED); + newLore.add(0, StringTag.valueOf(Component.Serializer.toJson(warningText))); + } + display.put("Lore", newLore); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/block/SuitcaseBlock.java b/src/main/java/io/lampnet/travelerssuitcase/block/SuitcaseBlock.java new file mode 100644 index 0000000..fc20eea --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/block/SuitcaseBlock.java @@ -0,0 +1,367 @@ +package io.lampnet.travelerssuitcase.block; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import io.lampnet.travelerssuitcase.block.entity.ModBlockEntities; +import io.lampnet.travelerssuitcase.block.entity.SuitcaseBlockEntity; +import io.lampnet.travelerssuitcase.item.KeystoneItem; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundSource; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.network.chat.Component; +import net.minecraft.ChatFormatting; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.Containers; +import net.minecraft.world.item.DyeColor; + +import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SuitcaseBlock extends BaseEntityBlock { + public static final BooleanProperty OPEN = BooleanProperty.create("open"); + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + public static final EnumProperty COLOR = EnumProperty.create("color", DyeColor.class); + private final static VoxelShape SHAPE_N = Block.box(0, 0, 2, 16, 4, 14); + private final static VoxelShape SHAPE_S = Block.box(0, 0, 2, 16, 4, 14); + private final static VoxelShape SHAPE_E = Block.box(2, 0, 0, 14, 4, 16); + private final static VoxelShape SHAPE_W = Block.box(2, 0, 0, 14, 4, 16); + + public SuitcaseBlock(BlockBehaviour.Properties properties) { + super(properties); + this.registerDefaultState(this.stateDefinition.any() + .setValue(OPEN, false) + .setValue(FACING, Direction.NORTH) + .setValue(COLOR, DyeColor.BROWN)); + } + + @Override + public RenderShape getRenderShape(BlockState pState) { + return RenderShape.MODEL; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { + return new SuitcaseBlockEntity(pPos, pState); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { + pBuilder.add(OPEN, FACING, COLOR); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) { + super.setPlacedBy(world, pos, state, placer, itemStack); + if (!world.isClientSide()) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof SuitcaseBlockEntity suitcase) { + CompoundTag blockEntityTag = BlockItem.getBlockEntityData(itemStack); + if (blockEntityTag != null) { + suitcase.load(blockEntityTag); + String keystoneName = suitcase.getBoundKeystoneName(); + if (keystoneName != null) { + List players = suitcase.getEnteredPlayers(); + for (SuitcaseBlockEntity.EnteredPlayerData player : players) { + suitcase.updatePlayerSuitcasePosition(player.uuid, pos); + Map suitcases = SuitcaseBlockEntity.SUITCASE_REGISTRY.computeIfAbsent( + keystoneName, k -> new HashMap<>() + ); + suitcases.put(player.uuid, pos); + } + } + } + } + } + } + + @Override + public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) { + if (!state.is(newState.getBlock())) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof SuitcaseBlockEntity suitcase) { + ItemStack itemStack = new ItemStack(this); + String boundKeystone = suitcase.getBoundKeystoneName(); + if (boundKeystone != null) { + CompoundTag beNbt = new CompoundTag(); + suitcase.saveAdditional(beNbt); + + if (!beNbt.isEmpty()) { + BlockItem.setBlockEntityData(itemStack, ModBlockEntities.SUITCASE_BLOCK_ENTITY.get(), beNbt); + } + + CompoundTag display = itemStack.getOrCreateTagElement("display"); + ListTag lore = new ListTag(); + String displayName = boundKeystone.replace("_", " "); + Component boundText; + if (suitcase.isLocked()) { + boundText = Component.literal("Bound to: §k" + displayName) + .withStyle(ChatFormatting.GRAY); + } else { + boundText = Component.literal("Bound to: " + displayName) + .withStyle(ChatFormatting.GRAY); + } + Component lockText = Component.literal(suitcase.isLocked() ? "§cLocked" : "§aUnlocked") + .withStyle(ChatFormatting.GRAY); + if (!suitcase.getEnteredPlayers().isEmpty()) { + Component warningText = Component.literal("§c⚠ Contains " + suitcase.getEnteredPlayers().size() + " Traveler's!") + .withStyle(ChatFormatting.RED); + lore.add(StringTag.valueOf(Component.Serializer.toJson(warningText))); + } + lore.add(StringTag.valueOf(Component.Serializer.toJson(boundText))); + lore.add(StringTag.valueOf(Component.Serializer.toJson(lockText))); + display.put("Lore", lore); + } + Containers.dropItemStack(world, pos.getX(), pos.getY(), pos.getZ(), itemStack); + } + super.onRemove(state, world, pos, newState, isMoving); + } + } + + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (world.isClientSide()) { + return InteractionResult.SUCCESS; + } + ItemStack heldItem = player.getItemInHand(hand); + BlockEntity blockEntity = world.getBlockEntity(pos); + if (!(blockEntity instanceof SuitcaseBlockEntity suitcase)) { + return InteractionResult.PASS; + } + if (!suitcase.canOpenInDimension(world)) { + world.playSound(null, pos, SoundEvents.IRON_DOOR_CLOSE, + SoundSource.BLOCKS, 0.3F, 2.0F); + player.displayClientMessage(Component.literal("§c☒"), true); + return InteractionResult.SUCCESS; + } + String boundKeystone = suitcase.getBoundKeystoneName(); + + if (heldItem.getItem() instanceof KeystoneItem) { + String keystoneName = heldItem.getHoverName().getString().toLowerCase(); + if (boundKeystone != null && boundKeystone.equals(keystoneName)) { + boolean newLockState = !suitcase.isLocked(); + suitcase.setLocked(newLockState); + world.playSound(null, pos, + newLockState ? SoundEvents.IRON_DOOR_CLOSE : SoundEvents.IRON_DOOR_OPEN, + SoundSource.BLOCKS, 0.3F, 2.0F); + player.displayClientMessage(Component.literal(newLockState ? "§7☒" : "§7☐"), true); + return InteractionResult.SUCCESS; + } + if (suitcase.isLocked()) { + world.playSound(null, pos, SoundEvents.IRON_DOOR_CLOSE, + SoundSource.BLOCKS, 0.3F, 2.0F); + player.displayClientMessage(Component.literal("§c☒"), true); + return InteractionResult.FAIL; + } + if (keystoneName.equals(Component.translatable("item.travelerssuitcase.keystone").getString().toLowerCase()) || !KeystoneItem.isValidKeystone(heldItem)) { + player.displayClientMessage(Component.literal("§cName the key to bind or ensure it is valid."), false); + return InteractionResult.FAIL; + } + suitcase.bindKeystone(keystoneName); + world.playSound(null, pos, SoundEvents.LODESTONE_COMPASS_LOCK, + SoundSource.BLOCKS, 2.0F, 0.0F); + return InteractionResult.SUCCESS; + } + + if (!player.isShiftKeyDown() || heldItem.isEmpty()) { + if (boundKeystone == null) { + world.playSound(null, pos, SoundEvents.CHAIN_PLACE, + SoundSource.BLOCKS, 0.5F, 2.0F); + return InteractionResult.FAIL; + } + if (suitcase.isLocked()) { + world.playSound(null, pos, SoundEvents.IRON_DOOR_CLOSE, + SoundSource.BLOCKS, 0.3F, 2.0F); + return InteractionResult.FAIL; + } + boolean isOpen = state.getValue(OPEN); + world.setBlockAndUpdate(pos, state.setValue(OPEN, !isOpen)); + if (!isOpen) { + world.playSound(null, pos, SoundEvents.LADDER_STEP, + SoundSource.BLOCKS, 0.3F, 0.0F); + world.playSound(null, pos, SoundEvents.CHEST_LOCKED, + SoundSource.BLOCKS, 0.3F, 2.0F); + } else { + world.playSound(null, pos, SoundEvents.LADDER_BREAK, + SoundSource.BLOCKS, 0.3F, 0.0F); + world.playSound(null, pos, SoundEvents.BAMBOO_WOOD_TRAPDOOR_CLOSE, + SoundSource.BLOCKS, 0.3F, 0.0F); + } + return InteractionResult.SUCCESS; + } + return InteractionResult.PASS; + } + + @Override + public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { + if (!world.isClientSide() && entity instanceof ServerPlayer player) { + if (!state.getValue(OPEN) || !player.isShiftKeyDown()) { + return; + } + BlockEntity blockEntity = world.getBlockEntity(pos); + if (!(blockEntity instanceof SuitcaseBlockEntity suitcase)) { + return; + } + String keystoneName = suitcase.getBoundKeystoneName(); + if (keystoneName == null) { + return; + } + String dimensionName = "pocket_dimension_" + keystoneName; + ResourceLocation dimensionRL = new ResourceLocation(TravelersSuitcase.MODID, dimensionName); + ResourceKey dimensionKey = ResourceKey.create(Registries.DIMENSION, dimensionRL); + ServerLevel targetWorld = world.getServer().getLevel(dimensionKey); + if (targetWorld != null) { + suitcase.playerEntered(player); + player.stopRiding(); + player.setDeltaMovement(Vec3.ZERO); + player.fallDistance = 0f; + player.teleportTo(targetWorld, 17.5, 97, 9.5, player.getYRot(), player.getXRot()); + world.playSound(null, pos, + SoundEvents.BUNDLE_DROP_CONTENTS, + SoundSource.PLAYERS, 2.0f, 1.0f); + } else { + TravelersSuitcase.LOGGER.warn("Target dimension {} not found for keystone {}", dimensionRL, keystoneName); + } + } + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { + switch(state.getValue(FACING)) { + case NORTH: return SHAPE_N; + case SOUTH: return SHAPE_S; + case EAST: return SHAPE_E; + case WEST: return SHAPE_W; + default: return SHAPE_N; + } + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + return this.defaultBlockState() + .setValue(FACING, ctx.getHorizontalDirection().getOpposite()) + .setValue(OPEN, false); + } + + @Override + public ItemStack getCloneItemStack(BlockGetter world, BlockPos pos, BlockState state) { + ItemStack stack = super.getCloneItemStack(world, pos, state); + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof SuitcaseBlockEntity suitcase) { + CompoundTag beNbt = new CompoundTag(); + suitcase.saveAdditional(beNbt); + if (beNbt != null && !beNbt.isEmpty()) { + BlockItem.setBlockEntityData(stack, ModBlockEntities.SUITCASE_BLOCK_ENTITY.get(), beNbt); + } + + String boundKeystone = suitcase.getBoundKeystoneName(); + if (boundKeystone != null) { + CompoundTag display = stack.getOrCreateTagElement("display"); + ListTag lore = new ListTag(); + if (!suitcase.getEnteredPlayers().isEmpty()) { + Component warningText = Component.literal("§c⚠ Contains " + suitcase.getEnteredPlayers().size() + " Traveler's!") + .withStyle(ChatFormatting.RED); + lore.add(StringTag.valueOf(Component.Serializer.toJson(warningText))); + } + String displayName = boundKeystone.replace("_", " "); + Component boundText; + if (suitcase.isLocked()) { + boundText = Component.literal("Bound to: §k" + displayName) + .withStyle(ChatFormatting.GRAY); + } else { + boundText = Component.literal("Bound to: " + displayName) + .withStyle(ChatFormatting.GRAY); + } + lore.add(StringTag.valueOf(Component.Serializer.toJson(boundText))); + Component lockText = Component.literal(suitcase.isLocked() ? "§cLocked" : "§aUnlocked") + .withStyle(ChatFormatting.GRAY); + lore.add(StringTag.valueOf(Component.Serializer.toJson(lockText))); + display.put("Lore", lore); + } + } + return stack; + } + + @Override + public List getDrops(BlockState state, LootParams.Builder builder) { + ItemStack stack = new ItemStack(this); + BlockEntity blockEntity = builder.getOptionalParameter(LootContextParams.BLOCK_ENTITY); + if (blockEntity instanceof SuitcaseBlockEntity suitcase) { + CompoundTag beNbt = new CompoundTag(); + suitcase.saveAdditional(beNbt); + if (beNbt != null && !beNbt.isEmpty()) { + BlockItem.setBlockEntityData(stack, ModBlockEntities.SUITCASE_BLOCK_ENTITY.get(), beNbt); + } + + String boundKeystone = suitcase.getBoundKeystoneName(); + if (boundKeystone != null) { + CompoundTag display = stack.getOrCreateTagElement("display"); + ListTag lore = new ListTag(); + if (!suitcase.getEnteredPlayers().isEmpty()) { + Component warningText = Component.literal("§c⚠ Contains " + suitcase.getEnteredPlayers().size() + " Traveler's!") + .withStyle(ChatFormatting.RED); + lore.add(StringTag.valueOf(Component.Serializer.toJson(warningText))); + } + String displayName = boundKeystone.replace("_", " "); + Component boundText; + if (suitcase.isLocked()) { + boundText = Component.literal("Bound to: §k" + displayName).withStyle(ChatFormatting.GRAY); + } else { + boundText = Component.literal("Bound to: " + displayName).withStyle(ChatFormatting.GRAY); + } + lore.add(StringTag.valueOf(Component.Serializer.toJson(boundText))); + Component lockText = Component.literal(suitcase.isLocked() ? "§cLocked" : "§aUnlocked") + .withStyle(ChatFormatting.GRAY); + lore.add(StringTag.valueOf(Component.Serializer.toJson(lockText))); + display.put("Lore", lore); + } + } + return Collections.singletonList(stack); + } + + @Override + public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) { + super.triggerEvent(state, world, pos, type, data); + BlockEntity blockentity = world.getBlockEntity(pos); + return blockentity != null ? blockentity.triggerEvent(type, data) : false; + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/block/entity/ModBlockEntities.java b/src/main/java/io/lampnet/travelerssuitcase/block/entity/ModBlockEntities.java new file mode 100644 index 0000000..786d0e2 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/block/entity/ModBlockEntities.java @@ -0,0 +1,25 @@ +package io.lampnet.travelerssuitcase.block.entity; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import io.lampnet.travelerssuitcase.block.ModBlocks; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +public class ModBlockEntities { + public static final DeferredRegister> BLOCK_ENTITIES = + DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, TravelersSuitcase.MODID); + + public static final RegistryObject> SUITCASE_BLOCK_ENTITY = + BLOCK_ENTITIES.register("suitcase", () -> + BlockEntityType.Builder.of( + SuitcaseBlockEntity::new, + ModBlocks.SUITCASE.get() + ).build(null)); + + public static void register(IEventBus eventBus) { + BLOCK_ENTITIES.register(eventBus); + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/block/entity/SuitcaseBlockEntity.java b/src/main/java/io/lampnet/travelerssuitcase/block/entity/SuitcaseBlockEntity.java new file mode 100644 index 0000000..6fde79c --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/block/entity/SuitcaseBlockEntity.java @@ -0,0 +1,246 @@ +package io.lampnet.travelerssuitcase.block.entity; + +import io.lampnet.travelerssuitcase.block.PocketPortalBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class SuitcaseBlockEntity extends BlockEntity { + private String boundKeystoneName; + private boolean isLocked = false; + private boolean dimensionLocked = true; + private final List enteredPlayers = new ArrayList<>(); + + public static class EnteredPlayerData { + public final String uuid; + public final double x, y, z; + public final float pitch, yaw; + public final BlockPos suitcasePos; + + public EnteredPlayerData(String uuid, double x, double y, double z, float pitch, float yaw, BlockPos suitcasePos) { + this.uuid = uuid; this.x = x; this.y = y; this.z = z; + this.pitch = pitch; this.yaw = yaw; + this.suitcasePos = suitcasePos; + } + + public CompoundTag toNbt() { + CompoundTag nbt = new CompoundTag(); + nbt.putString("UUID", uuid); + nbt.putDouble("X", x); nbt.putDouble("Y", y); nbt.putDouble("Z", z); + nbt.putFloat("Pitch", pitch); nbt.putFloat("Yaw", yaw); + if (suitcasePos != null) { + nbt.putInt("SuitcaseX", suitcasePos.getX()); + nbt.putInt("SuitcaseY", suitcasePos.getY()); + nbt.putInt("SuitcaseZ", suitcasePos.getZ()); + } + return nbt; + } + + public static EnteredPlayerData fromNbt(CompoundTag nbt) { + BlockPos sPos = null; + if (nbt.contains("SuitcaseX") && nbt.contains("SuitcaseY") && nbt.contains("SuitcaseZ")) { + sPos = new BlockPos(nbt.getInt("SuitcaseX"), nbt.getInt("SuitcaseY"), nbt.getInt("SuitcaseZ")); + } + return new EnteredPlayerData( + nbt.getString("UUID"), + nbt.getDouble("X"), nbt.getDouble("Y"), nbt.getDouble("Z"), + nbt.getFloat("Pitch"), nbt.getFloat("Yaw"), + sPos + ); + } + } + + public SuitcaseBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.SUITCASE_BLOCK_ENTITY.get(), pos, state); + } + + public boolean canOpenInDimension(Level level) { + if (!dimensionLocked) { + return true; + } + return level.dimension() == Level.OVERWORLD; + } + + public boolean isDimensionLocked() { + return dimensionLocked; + } + + public void playerEntered(ServerPlayer player) { + enteredPlayers.removeIf(data -> data.uuid.equals(player.getUUID().toString())); + EnteredPlayerData data = new EnteredPlayerData( + player.getUUID().toString(), + player.getX(), player.getY(), player.getZ(), + player.getXRot(), player.getYRot(), + this.worldPosition + ); + enteredPlayers.add(data); + if (boundKeystoneName != null) { + Map suitcases = SUITCASE_REGISTRY.computeIfAbsent( + boundKeystoneName, k -> new HashMap<>() + ); + suitcases.put(player.getUUID().toString(), this.worldPosition); + } + PocketPortalBlock.storePlayerPosition(player); + setChangedAndNotify(); + } + + public EnteredPlayerData getExitPosition(String playerUuid) { + for (int i = 0; i < enteredPlayers.size(); i++) { + EnteredPlayerData data = enteredPlayers.get(i); + if (data.uuid.equals(playerUuid)) { + EnteredPlayerData exitData = new EnteredPlayerData( + data.uuid, + this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 1.0, this.worldPosition.getZ() + 0.5, + data.pitch, data.yaw, + this.worldPosition + ); + enteredPlayers.remove(i); + setChangedAndNotify(); + return exitData; + } + } + return null; + } + + public void bindKeystone(String keystoneName) { + this.boundKeystoneName = keystoneName; + setChangedAndNotify(); + } + + public String getBoundKeystoneName() { + return boundKeystoneName; + } + + public void setLocked(boolean locked) { + this.isLocked = locked; + setChangedAndNotify(); + } + + public boolean isLocked() { + return isLocked; + } + + @Override + public void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + if (boundKeystoneName != null) { + nbt.putString("BoundKeystone", boundKeystoneName); + } + nbt.putBoolean("Locked", isLocked); + nbt.putBoolean("DimensionLocked", dimensionLocked); + + ListTag playersListTag = new ListTag(); + for (EnteredPlayerData data : enteredPlayers) { + playersListTag.add(data.toNbt()); + } + if (!playersListTag.isEmpty()) { + nbt.put("EnteredPlayers", playersListTag); + } + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + if (nbt.contains("BoundKeystone", Tag.TAG_STRING)) { + boundKeystoneName = nbt.getString("BoundKeystone"); + } else { + boundKeystoneName = null; + } + isLocked = nbt.getBoolean("Locked"); + dimensionLocked = !nbt.contains("DimensionLocked") || nbt.getBoolean("DimensionLocked"); + enteredPlayers.clear(); + if (nbt.contains("EnteredPlayers", Tag.TAG_LIST)) { + ListTag playersListTag = nbt.getList("EnteredPlayers", Tag.TAG_COMPOUND); + for (int i = 0; i < playersListTag.size(); i++) { + enteredPlayers.add(EnteredPlayerData.fromNbt(playersListTag.getCompound(i))); + } + } + } + + @Nullable + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag() { + CompoundTag nbt = new CompoundTag(); + this.saveAdditional(nbt); + return nbt; + } + + public static final Map> SUITCASE_REGISTRY = Collections.synchronizedMap(new HashMap<>()); + + public static BlockPos findSuitcasePosition(String keystoneName, String playerUuid) { + Map suitcases = SUITCASE_REGISTRY.get(keystoneName); + return (suitcases != null) ? suitcases.get(playerUuid) : null; + } + + public static void removeSuitcaseEntry(String keystoneName, String playerUuid) { + Map suitcases = SUITCASE_REGISTRY.get(keystoneName); + if (suitcases != null) { + suitcases.remove(playerUuid); + if (suitcases.isEmpty()) { + SUITCASE_REGISTRY.remove(keystoneName); + } + } + } + + public List getEnteredPlayers() { + return Collections.unmodifiableList(enteredPlayers); + } + + public static void initializeSuitcaseRegistry(Map> savedRegistry) { + SUITCASE_REGISTRY.clear(); + if (savedRegistry != null) { + savedRegistry.forEach((key, value) -> { + Map players = SUITCASE_REGISTRY.computeIfAbsent(key, k -> Collections.synchronizedMap(new HashMap<>())); + players.putAll(value); + }); + } + } + + public static void saveSuitcaseRegistryTo(Map> destination) { + destination.clear(); + SUITCASE_REGISTRY.forEach((key, value) -> { + Map players = destination.computeIfAbsent(key, k -> new HashMap<>()); + players.putAll(value); + }); + } + + public void updatePlayerSuitcasePosition(String playerUuid, BlockPos newPos) { + boolean updated = false; + for (int i = 0; i < enteredPlayers.size(); i++) { + EnteredPlayerData data = enteredPlayers.get(i); + if (data.uuid.equals(playerUuid)) { + enteredPlayers.set(i, new EnteredPlayerData(data.uuid, data.x, data.y, data.z, data.pitch, data.yaw, newPos)); + updated = true; + break; + } + } + if (updated && boundKeystoneName != null) { + Map suitcases = SUITCASE_REGISTRY.computeIfAbsent(boundKeystoneName, k -> new HashMap<>()); + suitcases.put(playerUuid, newPos); + } + if(updated) setChangedAndNotify(); + } + + private void setChangedAndNotify() { + setChanged(); + if (level != null && !level.isClientSide()) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/item/KeystoneItem.java b/src/main/java/io/lampnet/travelerssuitcase/item/KeystoneItem.java new file mode 100644 index 0000000..580a770 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/item/KeystoneItem.java @@ -0,0 +1,184 @@ +package io.lampnet.travelerssuitcase.item; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import net.minecraft.ChatFormatting; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.LevelResource; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; +import net.minecraft.world.item.ItemStack.TooltipPart; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +public class KeystoneItem extends Item { + + public KeystoneItem(Item.Properties properties) { + super(properties); + } + + @Override + public InteractionResultHolder use(Level world, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + String keystoneName = stack.hasCustomHoverName() ? stack.getHoverName().getString().toLowerCase() : ""; + String defaultName = "item.travelerssuitcase.keystone"; + if (!stack.hasCustomHoverName() || keystoneName.isEmpty() || keystoneName.equals(Component.translatable(defaultName).getString().toLowerCase())) { + return InteractionResultHolder.pass(stack); + } + if (world.isClientSide) { + return InteractionResultHolder.success(stack); + } + if (stack.isEnchanted()) { + return InteractionResultHolder.pass(stack); + } + String dimensionName = "pocket_dimension_" + keystoneName.replaceAll("[^a-z0-9_]", ""); + createDimension(world.getServer(), dimensionName); + stack.enchant(Enchantments.BINDING_CURSE, 1); + stack.hideTooltipPart(TooltipPart.ENCHANTMENTS); + stack.hideTooltipPart(TooltipPart.MODIFIERS); + CompoundTag nbt = stack.getOrCreateTag(); + nbt.putInt("RepairCost", 32767); + world.playSound(null, player.getX(), player.getY(), player.getZ(), + SoundEvents.AMETHYST_CLUSTER_FALL, SoundSource.PLAYERS, 2.0F, 2.0F); + return InteractionResultHolder.success(stack); + } + + public static boolean isValidKeystone(ItemStack stack) { + String keystoneName = stack.hasCustomHoverName() ? stack.getHoverName().getString().toLowerCase() : ""; + String defaultNameKey = "item.travelerssuitcase.keystone"; + return stack.hasCustomHoverName() && !keystoneName.isEmpty() && + !keystoneName.equals(Component.translatable(defaultNameKey).getString().toLowerCase()) && + stack.isEnchanted(); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltip, TooltipFlag flag) { + String keystoneName = stack.hasCustomHoverName() ? stack.getHoverName().getString().toLowerCase() : ""; + String defaultNameKey = "item.travelerssuitcase.keystone"; + if (!stack.hasCustomHoverName() || keystoneName.isEmpty() || + keystoneName.equals(Component.translatable(defaultNameKey).getString().toLowerCase())) { + tooltip.add(Component.literal("§7Rename to bind").withStyle(ChatFormatting.ITALIC)); + } + } + + @Override + public void inventoryTick(ItemStack stack, Level world, Entity entity, int slot, boolean selected) { + String keystoneName = stack.hasCustomHoverName() ? stack.getHoverName().getString().toLowerCase() : ""; + String defaultNameKey = "item.travelerssuitcase.keystone"; + if (!stack.hasCustomHoverName() || keystoneName.isEmpty() || + keystoneName.equals(Component.translatable(defaultNameKey).getString().toLowerCase())) { + CompoundTag nbt = stack.getOrCreateTag(); + nbt.putInt("CustomModelData", 1); + } else if (stack.getTag() != null && stack.getTag().contains("CustomModelData")) { + stack.getTag().remove("CustomModelData"); + } + } + + private boolean createDimension(MinecraftServer server, String dimensionName) { + if (server == null) { + TravelersSuitcase.LOGGER.error("Failed to create dimension: " + dimensionName + " (MinecraftServer instance is null)"); + return false; + } + try { + Path datapackPath = server.getWorldPath(LevelResource.ROOT) + .resolve("datapacks") + .resolve("travelerssuitcase"); + Path dimensionPath = datapackPath + .resolve("data") + .resolve("travelerssuitcase") + .resolve("dimension"); + Files.createDirectories(dimensionPath); + createPackMcmeta(datapackPath); + Path dimensionFile = dimensionPath.resolve(dimensionName + ".json"); + boolean dimensionExists = Files.exists(dimensionFile); + + ResourceLocation dimensionKeyLocation = new ResourceLocation("travelerssuitcase", dimensionName); + boolean isDimensionRegistered = server.levelKeys().stream() + .anyMatch(key -> key.location().equals(dimensionKeyLocation)); + + Path dimensionRegistryPath = server.getWorldPath(LevelResource.ROOT) + .resolve("data") + .resolve("travelerssuitcase") + .resolve("dimension_registry"); + Files.createDirectories(dimensionRegistryPath); + Path dimensionRegistryFile = dimensionRegistryPath.resolve("registry.txt"); + Set registeredDimensionsInFile = new HashSet<>(); + if (Files.exists(dimensionRegistryFile)) { + registeredDimensionsInFile = new HashSet<>(Files.readAllLines(dimensionRegistryFile)); + } + boolean isDimensionInRegistryFile = registeredDimensionsInFile.contains(dimensionName); + + if (!dimensionExists) { + String dimensionJson = """ + { + "type": "travelerssuitcase:pocket_dimension_type", + "generator": { + "type": "minecraft:flat", + "settings": { + "biome": "travelerssuitcase:pocket_islands", + "layers": [ + { + "block": "travelerssuitcase:portal", + "height": 1 + } + ] + } + } + } + """; + Files.writeString(dimensionFile, dimensionJson); + if (!isDimensionInRegistryFile) { + registeredDimensionsInFile.add(dimensionName); + Files.write(dimensionRegistryFile, registeredDimensionsInFile); + } + } + + if (!isDimensionInRegistryFile && !isDimensionRegistered) { + Path structureMarkerPath = server.getWorldPath(LevelResource.ROOT) + .resolve("data") + .resolve("travelerssuitcase") + .resolve("pending_structures"); + Files.createDirectories(structureMarkerPath); + Files.writeString(structureMarkerPath.resolve(dimensionName + ".txt"), "pending"); + return true; + } + return false; + } catch (IOException e) { + TravelersSuitcase.LOGGER.error("Failed to create dimension: " + dimensionName, e); + return false; + } + } + + private void createPackMcmeta(Path datapackPath) throws IOException { + Path packMcmeta = datapackPath.resolve("pack.mcmeta"); + if (!Files.exists(packMcmeta)) { + String content = """ + { + "pack": { + "pack_format": 15, + "description": "travelerssuitcase Dimensions" + } + } + """; + Files.writeString(packMcmeta, content); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/lampnet/travelerssuitcase/item/ModItemGroups.java b/src/main/java/io/lampnet/travelerssuitcase/item/ModItemGroups.java new file mode 100644 index 0000000..d034ba3 --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/item/ModItemGroups.java @@ -0,0 +1,52 @@ +package io.lampnet.travelerssuitcase.item; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import io.lampnet.travelerssuitcase.block.ModBlocks; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.ItemStack; +import net.minecraft.network.chat.Component; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.RegistryObject; +import net.minecraft.core.registries.Registries; // For DeferredRegister.create(Registries.CREATIVE_MODE_TAB...) +import net.minecraft.world.level.block.Blocks; // For Blocks.ANVIL + +public class ModItemGroups { + public static final DeferredRegister CREATIVE_MODE_TABS = + DeferredRegister.create(Registries.CREATIVE_MODE_TAB, TravelersSuitcase.MODID); + + public static final RegistryObject POCKET_GROUP = CREATIVE_MODE_TABS.register("pocket", + () -> CreativeModeTab.builder() + .title(Component.translatable("itemgroup.travelerssuitcase.pocket")) // Changed itemgroup name + .icon(() -> new ItemStack(ModItems.KEYSTONE.get())) + .displayItems((displayParameters, output) -> { + output.accept(ModItems.KEYSTONE.get()); + + output.accept(ModBlocks.SUITCASE.get()); + output.accept(ModBlocks.WHITE_SUITCASE.get()); + output.accept(ModBlocks.LIGHT_GRAY_SUITCASE.get()); + output.accept(ModBlocks.GRAY_SUITCASE.get()); + output.accept(ModBlocks.BLACK_SUITCASE.get()); + output.accept(ModBlocks.RED_SUITCASE.get()); + output.accept(ModBlocks.ORANGE_SUITCASE.get()); + output.accept(ModBlocks.YELLOW_SUITCASE.get()); + output.accept(ModBlocks.LIME_SUITCASE.get()); + output.accept(ModBlocks.GREEN_SUITCASE.get()); + output.accept(ModBlocks.CYAN_SUITCASE.get()); + output.accept(ModBlocks.LIGHT_BLUE_SUITCASE.get()); + output.accept(ModBlocks.BLUE_SUITCASE.get()); + output.accept(ModBlocks.MAGENTA_SUITCASE.get()); + output.accept(ModBlocks.PURPLE_SUITCASE.get()); + output.accept(ModBlocks.PINK_SUITCASE.get()); + + output.accept(ModBlocks.PORTAL.get()); + + output.accept(Blocks.ANVIL); + }) + .build()); + + public static void register(IEventBus eventBus) { // Renamed from registerItemGroups + CREATIVE_MODE_TABS.register(eventBus); + TravelersSuitcase.LOGGER.info("Registering Creative Mode Tabs for " + TravelersSuitcase.MODID); + } +} diff --git a/src/main/java/io/lampnet/travelerssuitcase/item/ModItems.java b/src/main/java/io/lampnet/travelerssuitcase/item/ModItems.java new file mode 100644 index 0000000..0a033eb --- /dev/null +++ b/src/main/java/io/lampnet/travelerssuitcase/item/ModItems.java @@ -0,0 +1,20 @@ +package io.lampnet.travelerssuitcase.item; + +import io.lampnet.travelerssuitcase.TravelersSuitcase; +import net.minecraft.world.item.Item; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +public class ModItems { + public static final DeferredRegister ITEMS = + DeferredRegister.create(ForgeRegistries.ITEMS, TravelersSuitcase.MODID); + + public static final RegistryObject KEYSTONE = ITEMS.register("keystone", + () -> new KeystoneItem(new Item.Properties())); + + public static void register(IEventBus eventBus) { + ITEMS.register(eventBus); + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..a1fc7ec --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,26 @@ +modLoader="javafml" +loaderVersion="[47,)" +license="MIT" + +[[mods]] +modId="travelerssuitcase" +version="${mod_version}" +displayName="Traveler's Suitcase" +authors="BennyBoops, Candle" +description=''' +A mod that adds magical suitcases that can store player positions and create pocket dimensions. +''' + +[[dependencies.travelerssuitcase]] +modId="forge" +mandatory=true +versionRange="[47,)" +ordering="NONE" +side="BOTH" + +[[dependencies.travelerssuitcase]] +modId="minecraft" +mandatory=true +versionRange="[1.20.1,1.21)" +ordering="NONE" +side="BOTH" \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/black_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/black_suitcase.json new file mode 100644 index 0000000..3303b20 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/black_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/black_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/black_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/black_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/black_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/black_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/black_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/black_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/black_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/blue_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/blue_suitcase.json new file mode 100644 index 0000000..acbb457 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/blue_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/blue_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/blue_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/blue_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/blue_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/blue_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/blue_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/blue_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/blue_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/cyan_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/cyan_suitcase.json new file mode 100644 index 0000000..c852799 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/cyan_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/cyan_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/cyan_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/cyan_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/cyan_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/cyan_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/cyan_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/cyan_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/cyan_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/gray_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/gray_suitcase.json new file mode 100644 index 0000000..cb927c4 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/gray_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/gray_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/gray_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/gray_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/gray_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/gray_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/gray_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/gray_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/gray_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/green_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/green_suitcase.json new file mode 100644 index 0000000..842c0b5 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/green_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/green_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/green_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/green_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/green_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/green_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/green_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/green_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/green_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/light_blue_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/light_blue_suitcase.json new file mode 100644 index 0000000..544b290 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/light_blue_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/light_blue_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/light_blue_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/light_blue_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/light_blue_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/light_blue_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/light_blue_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/light_blue_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/light_blue_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/light_gray_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/light_gray_suitcase.json new file mode 100644 index 0000000..daaea82 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/light_gray_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/light_gray_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/light_gray_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/light_gray_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/light_gray_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/light_gray_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/light_gray_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/light_gray_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/light_gray_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/lime_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/lime_suitcase.json new file mode 100644 index 0000000..9a32b48 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/lime_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/lime_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/lime_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/lime_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/lime_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/lime_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/lime_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/lime_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/lime_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/magenta_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/magenta_suitcase.json new file mode 100644 index 0000000..532e3fe --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/magenta_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/magenta_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/magenta_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/magenta_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/magenta_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/magenta_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/magenta_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/magenta_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/magenta_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/orange_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/orange_suitcase.json new file mode 100644 index 0000000..28984df --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/orange_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/orange_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/orange_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/orange_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/orange_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/orange_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/orange_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/orange_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/orange_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/pink_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/pink_suitcase.json new file mode 100644 index 0000000..04ffd96 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/pink_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/pink_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/pink_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/pink_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/pink_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/pink_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/pink_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/pink_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/pink_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/portal.json b/src/main/resources/assets/travelerssuitcase/blockstates/portal.json new file mode 100644 index 0000000..51c3835 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/portal.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "travelerssuitcase:block/portal" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/purple_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/purple_suitcase.json new file mode 100644 index 0000000..0699307 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/purple_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/purple_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/purple_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/purple_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/purple_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/purple_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/purple_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/purple_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/purple_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/red_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/red_suitcase.json new file mode 100644 index 0000000..17f5cbe --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/red_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/red_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/red_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/red_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/red_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/red_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/red_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/red_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/red_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/suitcase.json new file mode 100644 index 0000000..8de297e --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/white_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/white_suitcase.json new file mode 100644 index 0000000..80bb669 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/white_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/white_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/white_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/white_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/white_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/white_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/white_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/white_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/white_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/blockstates/yellow_suitcase.json b/src/main/resources/assets/travelerssuitcase/blockstates/yellow_suitcase.json new file mode 100644 index 0000000..f20a6d8 --- /dev/null +++ b/src/main/resources/assets/travelerssuitcase/blockstates/yellow_suitcase.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,open=false": { + "model": "travelerssuitcase:block/yellow_suitcase_closed" + }, + "facing=south,open=false": { + "model": "travelerssuitcase:block/yellow_suitcase_closed", + "y": 180 + }, + "facing=west,open=false": { + "model": "travelerssuitcase:block/yellow_suitcase_closed", + "y": 270 + }, + "facing=east,open=false": { + "model": "travelerssuitcase:block/yellow_suitcase_closed", + "y": 90 + }, + "facing=north,open=true": { + "model": "travelerssuitcase:block/yellow_suitcase_open" + }, + "facing=south,open=true": { + "model": "travelerssuitcase:block/yellow_suitcase_open", + "y": 180 + }, + "facing=west,open=true": { + "model": "travelerssuitcase:block/yellow_suitcase_open", + "y": 270 + }, + "facing=east,open=true": { + "model": "travelerssuitcase:block/yellow_suitcase_open", + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/travelerssuitcase/icon.png b/src/main/resources/assets/travelerssuitcase/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..63a76e64d645e2c0296604199696af0a90c65bfe GIT binary patch literal 17015 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/black_suitcase_open.png b/src/main/resources/assets/travelerssuitcase/textures/block/black_suitcase_open.png new file mode 100644 index 0000000000000000000000000000000000000000..0484f969749729e6ce94c14ce269044cf86f7159 GIT binary patch literal 3952 zcmeHJ`9D;9A3tZtj2Vq(#?V-2BCcC*4Jun0VwARNQHpDhA#_th64A^UTZ?epgv!!# zWoc9-qOpXF8VYHgYx1Q@Sc%C25Ij?hG=k@-)KcCP0^ZtB~(>6IP zDrhPI0HC;Tt>f0YC+q8lLeAZ5Y`vNQK-qkqqy6?H8I0}t{#iatu2MXEvUyKePZy>8 zp+uO-CXgDMVzWQIN+LpO>4_RnLa}>KbB4IJ^X)T%INDM6M@ZZw^_NEB(ZS0k?c)~&?+ck>i0`W^`BM{zxQNe_Q3 zi1aXk%`wRph?I+o7_)`G%0(dxIjp#O0#^?rctF-FAj=cXUkGlSIs-m^D_L+!pY>S% zm~cGkYJC+CL?XWm@V_`mUjVp8krO6N0`FX}SE$WwDOe^nBE-xSiNP-LpP59P+DVA3 z&<&^`v5TDw6;%>uauBO`VPRtF7Rnrfe`GKR zxkrFen;u8}YPKuu4u>8z@!U%kPT;j8S?pVZq<&$1A*#nq>$QH2K5U-OMNh$?eG||f zZ%HMCpY@j69oQanJFBozG({}yK{XNsEQuXR20?oxZWcBljhS(+(DtPPZ>-H$T{x*#lw5 zMu}w#LMv#J=K`D1vC&tMrd@5R;iyQ?U4;ucas2#>)RGg!8m(-bK>!Yi|GT*id;Uxe zXdI8#wct3dMrGWe#5;}D6W6+fzIMj-5>A>j{{?Md%TTZ!<=k}A$ZG2LWgB!W7Js#t zrp!^{B6S`W?*;gdWP18F{590sd(k}L;b6L3fXzU zk3jgfoTEV?{Hf0Dy~}|e!yJDV-JcLh4QZ_G-DqCIjsa79jlpX`VH~L$n_3Xzu_SBt zp+5gm+X!^5CyZ2wNPyS;GAgY__K*b-vF0EiLzco+zd}t+9Ab1BnFs0C-QVV#6<95~AB zJ%C}rOL0IE-1?m%V0ia{Zh*T*5#;6yN?o^lH;ff*YovS7$rf&c1@^5zbE!2UZ^*>i zfNPDiRRT6b?)np-zK#C<1KQ0lQnNGW}Wj(sBdA?`DTn~hZdQ^T8nNli8S_VSF z<39iv#1DmY26_7RS*fX1??$j(0J^&?QdRBPG(`#JQyx;wR8t!~-(x&0 z$;qwv@AI|*@{IIfdLWt~V1Gu)BsPa|B8nceE-tk#7IQf}AJe-3VnW?(GF(4sy@j^) zKL)!vF2%X-`btN$*B-Zzd;LDVI_4#v&efzG^aVPWLXP}KS`@(memJxgOmVVJLp48a z%#MW8sXh*}Twf^4^+l*8th*$)4AJH-9DHB6g`zA?hJo+#l^+1!;Yjq@3tobuw_`Y9 zyU;sNQUp3AI!xrF^90=n*q1ZW7I#xT25NwO$exJSPBD;5p!(%Hc;94nHFP$`npQI> z1v44;*>}BiehL9YFv@;Vr8DxAWhydMF>Ivsq&fcJ6Sd4>4GTBY9Y2rZ+cGxxlfqkLl^VgcKP6r5G9EW7?BK!;{2MzhltTy@wfI!)d$}D^Fs6EAt zKO!>f;aZ(-Nu6ch#cI3N)&L8wwKf=}Mt(-z-YOq8fQ=Xy#wHHh&y?zE>!5f7k;<3A zOzSI1!2_F((v!lcQQ!-e2 zqi0EOY0FE%gLd?7ln&cZ-Rz%Nar9daId{cEvk1Ukc7z`O=g20me?KldTuV2S2$GyCQFO0xgUPYM>q&0wa;_<&Z^YlbmEEA6yK0_XstIVvz=~}z zUE)ADo#V$`2=Yq|HwyYZ8oK&-C>{AB4Mcfcri}D@=+bhZWMn5z7c7@6Hs>e9C|MI z+{#*|=T&Vf?hdD5dh;ba2U~JpDeBp>`+0j!CKA>0SK%^S_?AI9nss?w>rOdr9`U(u z-4+Ts&){l~-Ji2My4r@{GAKLk6dLC3%~AB2@LAY{H6W3hS*Ukx>KwS7o{)amolgaE p^I<>I*N|=<&Hukh{zq#72Lw9fi_Hx~{`h)ST<5gOvCsjE{uj)5D^>si literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/blue_suitcase_closed.png b/src/main/resources/assets/travelerssuitcase/textures/block/blue_suitcase_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa653122a6d8bb51bbf86f7d2d339a0927c7ee8 GIT binary patch literal 4228 zcmeHKiB}VO67C!X5=c0e1c88nG7M-Why+FmH;Rie%jrdf7mlKw9vDm~AfQGF3dkXH zWM*|1P*enU6ca!|MTv^Szz9(U@jxPXm?XXNjYGuu*neO*?{)h9x_@0?)mK$tS0D5D z^Ty*0Z~y?{*Z6q+s5nEP4>gS9ztK730RU+EuJLdUJa{&88wgIfQy}_d{(`%4#sMmN z@)9Of?3np!YCT0(l512Uy%XLSFn!1Q&fVSTN>goduu}(P6QujFcWU{^a`8xb7arFy zz!Hf>g>3+aBMsUzdFY`N$)uZ^=k-Al{BoIOO<)Z$mPE0}VRGsY$&~&&IOPPK5;dK* zA!fzmfxy*o`@n9WcS{>3>0CScl#2&>(IrZLS}yu5qg+e=U<{mjDq9N9XlCgJABc-R z!4vl-A4JE#IkOWiBw0OLvezfcM7W4Tq5K7Bu-^i(94!b76?FL)oh<7<5v_IoCoU3$ z`OJXAO9QbzbaA&8j=Tja^|ryv;q)36xvMXFTn8W#?^7uUNw^jH7sS-FHzxI#J-s=s zf6E+_-aP^CwYyZ4|Sx{)S;)ugNTk+ANgWh?PdYJpsI zsSbbvZ>ROG_k(u*i^&}nR`r}wP0akoYL-%i+S^b@v);kimoAi0MIT8y5Q~NQ65SS{ zvP82Y1#Z0JTm!Nfs%gL?QnB_i4=aE>X0u2$AL;qS1wA#5DUW+Hji}pc+Um3UGpM!T z7dVkgfpl$6E0wN;e0*a7gi+p3z(U#CSatzXkk8UqoC*X)({_z!(PB3CTKC=;VgiR$ zng|a*T`t(uHO;q#zT(^tLbw{Q0Yj*N&4$nvWgu> zi6%yi{~QUYcwtI^1nkqoUrnoD%hDXA*hII1IEvoroem0g9@k;y&5w(UzpeMRqoMHa_5Tyv=?<&rDLHGEM z@1nMe%2AqAxHB+;qMW?7P%~J?WN?(moq?H;*Flv6Ry&@)6Q7N)Ms^)H>B1RnBlf3l z44islqN)rg$AbR4rcRh<`aSA)Cda-4qYyM@JMj9v$w>hzDx|uWt@hc5;@0mDobh8zy{%E zJBt`8$Q}EgW_o&si(FLVzLRySGyNEyczxk;!w1(KwALrhk}a%V&Fi_Y2z9hQhotyr4200 z?4A@YWYrke*RyK0K_@2Yl;mYTAMi`jD-;!^$z8e~Xi1w1Z+M-b#0wmSc6ZfyrDlmj z`80WnCHbb6er@MRGI`s1f|IIw3p_o8Iyxvy%h4FJXc%E~?Quz&>4+uv8HeRE_I1tY zwW7%M=_15WaK)K7q`;1qFmD9w(Wl-jR(*n#OpfUurzEXG_AEL$EW9Fcx-*-{xs7T}N`c_TF_-CUtrH}sDoSDbCeI4Vta{QCrOS_Z zPHk6f?-rgVEC9E$6|a!Mqt(6W1P|>_f)VFt3H=y=q9nGRaM))6YdGNj{wBXgMD^e^yvZq2#BYgZGKK^Bxjj_XpZ;sQ1ugU`(q?G` za530r8MH-*5A0z8!8)a=t_<@J9Fu>aMESV2tKD3k(SuaB^&F6fD3mni*#!L9PXv05 z+~ajhC}Ogw;bSK#*ZhJRi#X}-{Q;yc%uB)6B1+@YLp0e)7Sqzs5xVxgfN0_>z!aZ` zST0eF@(@DbEx(*Ah_9q~4p1a(pvk5B2(2>CtD=##Vn$+Zd0+HS_?~f{#yyKw@P%!B z_oT1Sv?|_yD8pEh{fBRO?b@XZe-bIsx&DZ&NBjCppT9Jl(V{6|&EFtAW3|OC0cTvs z|8Vje9~%|X4FBwMW*9<<)o-@0-%2?gmS4JCwJbV)`M!GgE_=U@TM~HrPm6%t{{V`7 zp=%_t9~U#R&;Oa(hT+B;j)qhzcE8t?%X$Q&*$a_@K~(fkqv>w?uGJJ7c^JKJJXuBicFu4s2R=C={FOJnHqq8Jz$bS5!+5hu$#`Murs>LcaX1 z=i52AQfHqw7*^qIn_Wdp8ZFp5XqS4|cWXD%G?UR;{shkuxM(BtQqqpa!xre68|qKx zQ|1LumB8%mF_&3%RjMZeb!FpAkJ_7$_dVfmWq-gkS~*D}npEA`pwaLl#w}@h>&I8# z@HYR~5my=G$OC(~kR7A3^Q5Z55;26&xLu@5h{>_5K7|Xphq~&WqHlX7wx8N92Z$zD zQcrJ9IZFkosyq6)R*3mpKO*-sKm`*owv9M}GuR4Tep*$o89Ki!Aml@75vrmhVpW3H zp4}p3wIS|d_Rvhs@I=b7K9IlV6&ELrmvhmxO`&k8+(?DQuvW30)V$V4Qn6FsV_#C$ zlHhlFGNq=E_<{z$$>@MpKAh%VWUkL2FDn{5<*V*#G(#}-^6O|qZrpw%K6hE+bjXk_ zStH6qz044|&z;*dX!7Kot8(&fAmV!Cq^O`j*4o5-A@H+8!yX~Xn}FdGX6krRxO=>{ zQ$nzKDJ$Dye^h4bTV2_HeB?5hrcI$e2DOT+#fxF54Q$r)35U)e-oU!hOzFfVX3GJq z>SWj0rH=GE&D+}|p*$cGHl@k$>32L6xZ{-L!kh|WXQB9U=1I)LC)xDt_EAR1od5J5 z3bb_tWF0GrrMZ&2eZ^$wt7)@r%@zH~K)V=E64rb|&PWjV+q}EMY!k)<5&iffsA~l? zL98F$xoi)E6>8F~O4G$+yj~{(^OQy8ORcoFbAo1+=>23_jp#rtse7NZR)|iu+nF#{ za5ogUpz@^Y^jlkpGlHgHjXwVTXcwo$Fbg?Pb(9}k1`Vv+(0;0HY6l1;H)&&x&%SVa z<@*`^@7aD%nU_nh#CSa7jqDBEi=a_t3yL+1sKn2DHakqt*>13Zk-J*e_a;d%1U|q# zd&&=CDMH#_yOw^$JUa}I6uHN?1vk8r9DA|(2c3C3P+z%?;bL#@l1F1 zuK6N2*dGmaNn}G63~z7X@_cN6_RatOK!gS2;%YWep>2xamyd#P;*171jln~s|7}lg vOVi20gvIl?Z*bR)q^!OFFW~=CLQue`SdG*}^_{HGUuSDP{X8zZf$aYPW5&Gc literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/blue_suitcase_open.png b/src/main/resources/assets/travelerssuitcase/textures/block/blue_suitcase_open.png new file mode 100644 index 0000000000000000000000000000000000000000..61a958e68d7bbb4b1f585ddde81555838fdac22a GIT binary patch literal 4346 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 W)k|^-fmVJ$B(7bv(Yb&?jQba^^!Vif literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/cyan_suitcase_closed.png b/src/main/resources/assets/travelerssuitcase/textures/block/cyan_suitcase_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..56289b94f239333d7c633742eb9ed28d0c6082b7 GIT binary patch literal 4268 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 KbGW&Q9`g?oBemcF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/cyan_suitcase_open.png b/src/main/resources/assets/travelerssuitcase/textures/block/cyan_suitcase_open.png new file mode 100644 index 0000000000000000000000000000000000000000..dcdf3cbb29720555e30af03d7f929377c3b74a19 GIT binary patch literal 4377 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 Ww6uBUJX!c>vtQ%3o_d2qkN-P<2KLAR literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/gray_suitcase_closed.png b/src/main/resources/assets/travelerssuitcase/textures/block/gray_suitcase_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..9e1b33d58edcd3e5948529d5d5c7a88d1d9ec257 GIT binary patch literal 3617 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 J%vwK~_ivz!XdnOp literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/gray_suitcase_open.png b/src/main/resources/assets/travelerssuitcase/textures/block/gray_suitcase_open.png new file mode 100644 index 0000000000000000000000000000000000000000..76c1df3d63f09a5123d88e5d41a6a994853e6416 GIT binary patch literal 3680 zcmeHJiB}W%7M{tL1 zG7Y>2007H2Z}Ql#ytCh*YAEHk+9jj`05Im8JzRGlzsQKbV<&HBBOGQtv+hD^3VHsR zqI`m+4ac!gy0+HVz^%WH*>Z`dExJ64x_vhDdX{5mQZD?<<~nGz)aS6sW(saQ$V=fR zzTR-`q1)uZwF9rA>>QJk`FF!s+1*k7vi!30s~R{Q?hl(kCb{))57~r_JvR|TmX1oQ z==gKLh5$uoGtD`s5Kwvk5sIsX64#Ur2Kd$@9V%#@YD4Ut zGy05?_NwDg3hddpJRd%cx9uNhp_Ip8S|PB#nS5y=;^pDVZorZKQs%0ePmE{zJ#zO} zQ|YmoGVu;bCo*|K{LHH2(u930VM_k1bMvG2UpqAJ%XY45TAc>N!(%QDdcxvp#BoP4A=>bi% zWZ^+{)k*nRwp@|FT3symZ ze|TMZ!T`XI9~w#U*bEiXvw#J{xD`OdT^=EwFrIH2GBy5%#jrTar4%Fvj-qz!@?+5l z-UhfCNZ6$ZsCf<4V_S`%*$lq!-*6N7U{c{{>cE<5!DarfABryX>2Ou)6n;k3{Cao<^6-IvQU z*ZK>GqiJw$=p++7j~KJ#1J|p~V?81Q;3_wSIlv5AA9r$I>g{I=a9Ytio@?Xaiva_X zY*RzT(*TQ9nLv$t1d6+}5w&iC&jO^9jZlP`89~Xe+8(V=0GDFm)~CvPMvWZ|WeF0} zzt{KKIb*WS-mWgJ>snG~{T;OjzjzP+vf96}*^2LrGWiGeBcl34wh5o10KK`Yhoq9+ zdzpQb+%KTMse10C=Oj4s9zw;<5VR>U<=D`Q$~d$`e_yZOr%oqGUbA%f`RMTUqVrUe z;$8qQaiv$No9Y)~hbG%oe(&lV!DV)2G1|GyR%2)(AB7hH>RrWYuC_ZZ))<%cHEYW- zD*<5staGaq=Fs^`ks+v5?n93fK3j%(8B6xw`0Z(@fOe&3L{+gcq>|+~ts4rN%I)v;uItr)`@L`YSwzL|RRZ9ZdO=crTgEDtazZC8 zv_&odq845U0fu|ZEpTS4Z;9qWK!!)K$>ghFgHbN)y}sJkL+czLim_kMzHxgD9exaU z+flF0DEUdzf$Y@X#`zLUwU@_n><)PO7ao}!s`NI!#z4OZ{z?Tb+=J|W+|klxbumF! z)3G(bBg4Xm8<_EOfGF6=}Vy7l{`bGqleKTxuVP2ug>|GtM67r+Y-J_4#yKHXGDb$Fv#{5M$z!-(x zC{u6Rn}aQ0LFWt|UUa8;2n{#6|FBo^AzcO{wJWPxdMWlqFxTaipMw=wi5 zY?rLyCLRZ=icUC$@^VzON|25xhYap|3|%=#E@CezXnOpZF`?H%AK~wNsaJpBaMD}GQ(Yrx zYyGnbjpnP=ceyq;k}D^Xf=53T1SR$QlTbnrjpFs88>%x>%aLE}}c`hBQXRe7SdL?7q3bqtISx03R}c%<#< zL7NHh7Y`Sj_eWry*6&E{-@4;zBcI#Y%d{MK80n?2A&9;4IZwrH1_=Ujc=E8&);gtI zp`7^!hPJR4HkQRmg(u2fgO35ZhNsQ~ydW6FkF+UiRVbRbVVPq=Kwv#DqO(b?%igJq z%tyaVe|E+lnCFp^lTBdRT_Bn{)K8S%XML^OG#0PNkNsHL`vzsvLeZ#OYV;~Rp-zp) z)cT#TJ~S_CGfeAt_CYQ|j~}xG?%H#81M4?xaQ_C6eY!fu@On}@R@m0-*Rj7y**lcS zUyZ1R!3IFvWA94;tmmyijeSR{n6^rT301vju{TOf*_W!Vj#{qVPI>joBR{H@s%bA? zh#!jpaMa)xK4C-J?(-JLh6`!AXh1HESLb~;7)00~H_4o;85g$`GGMw(N%=ETe*xzI f4+{MMrUr1J`It`^jFFY|e*W9+>FZJEMo;`5Kwr3_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/travelerssuitcase/textures/block/green_suitcase_closed.png b/src/main/resources/assets/travelerssuitcase/textures/block/green_suitcase_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..3e1e25c22f96cbf3499dac7c588877ebf877696c GIT binary patch literal 4163 zcmeHJi$7HR7T;sW44T2JWfu{=^m0wV{$Y|o*`kj84TrJp@$lr zTZ(W?DjjErnJ&sJhe)T8n7JtibvzoguRgcW;Cyc9AGmjaK6~%a-ut)L`mXQ#uJv2# z)ZMNa6g4UIvWCfzTV#Vn0g8%i6t*R%Jtp5B+j^5v zQ$VB9i-rLRqU*M1l_Hd&aNWAB>#J!r+LzZl7OF`j@d1pa4+J0e>III(Z$!aE3NgDc zE1N4*C^-$^c+g=QPoQsru*Nrv>%%=A5*Wveh9=g27XI+5${h%BLB=U3Ei|BMEo54`4 ztttdx1!OeUz;BR)3c#pU?jS zo0LfjVt3A4+)pppyKZTgIIXFv}^>=UNyJhx-DmHUvO2 z?y1>{N?m1XsHGf9l|gL(W>6cJL6kvIb5|N2eZvsgDGL>Y`VqZhS`?U^-Y8{m+dZ6B zEIhdR(53Hd_jBD)rmUyUP0PGU5TNf2+lWAlg~NlZcD#WaBJ7Q8Vu1H}DLWtG0a53O zp!t-$zC`Z|n3M!t#I`7dsCwW{GeMEGOgR#SWNd&*bU||W0-I9_6&guhO-GYoL-3ag zI;zY53;{lD0EE7$VkkwV_+>~v`E?J?`6PxR%1edFY8t~e;I@&Ph0T1S5NM#TaapSr zpzy@fif?ES^eo(e3*~M`F6Nj2g{yNfDbDNpWTk`F8A~=7Ax&BStnTQ0234+*Bbw3~ zVngD!M8_7Wtjo5?37GGv;Jx-Ek*UG{pN_aKliWE|O3$cotRN>dPq$W@geXT(5094fYXaP=@}91eQD<&n9SR}uusA39nyUg8~Ik-Z=J4gPl2Tb1+k zdmi%#(E^ic_;}73E;p(BEzHGAu0?vo`Dr^_!!~v(Q<8n}AC`_0^URu?dc7rw3b#x0 z02MrWRKXfYaQY7EwOe|^##%ExumD>ZH1p{?K|N;2L&e*-2hzI86-ykhw4OBL=AFg4 z^@x2-=*Ka|_Sq*O;3cZtMS|H*7dzt`J=9o}pdUdzHNXUYBsj-#Nf@`VsUf8dEoQsw`gg*r|^d|X@lqb1m%Q(Nqeuf+! zjw3kGo6;N`cCpAAIV9A9^Q&G2H$6fHYF0;5BBa?>WGX}TxLXV8Yb2GS{~y>|_%F<;hGxKeOT!W1nlxvbVpjN~H`v19`--yHVl-W^ zmir~&lNFQVu)x@MimO)c%tvE*M%`6PIshl0XuYgH?H7W;ftiG`Y06llI5zp5Y!m4$ zbsxbP2)csRV8?l&?eSBqUK512O-C!n^|h9&a)r;vLX0%qOEmmepC3Vr55~KU5$-a; zmPm@;6LP1O?64SA(5pGGTK>=vq-MEJ*zkc06YwK4F(KugQuVWK$Mmc$L{`;#`RfKv zg`v_rg=~$lh?P|U9IL(zK6~6Go)ARXW+ggY&h|B;C>GK*I=#q7>q-<3-by?O)GzYb zH@oPBf|5K?7UOqJHNmL6ED9fpI$odngTJh9SBAlWOPwYr1T%v};+EmJqEZZ+SbT%u zGtbHFYlyJ0KxrO>Q$$Lgk@Euth{}ip9n9y3?SgW)F|%$CNuOssXTB>U@65OfTh`=O zIG`tu2oHBA+F8E~!`Ia5)gO1QnzrmmW-#ms-!=TxfVN)Et3W1GH7Mm|i5tFC=!