From ceb59bb6254c61e3ec851f5f2259ba8015e8e365 Mon Sep 17 00:00:00 2001 From: LiuEnder Date: Sat, 25 Apr 2026 19:02:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E4=BC=A0=E9=80=81?= =?UTF-8?q?=E9=AD=94=E9=95=9Cmod=E5=AE=8C=E6=95=B4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 回城魔镜:传送至出生点(优先床/重生锚,否则世界出生点) - 传送魔镜:打开玩家选择UI后传送到目标玩家身边 - 4个等级(初级/中级/高级/永久),支持耐久、冷却、副作用配置 - 8个合成配方 - 支持中英文语言 - 修复配置加载顺序问题 --- gradle.properties | 16 +- .../java/com/example/examplemod/Config.java | 42 ---- .../com/example/examplemod/ExampleMod.java | 131 ----------- .../java/com/le/teleportmirror/Config.java | 65 ++++++ .../com/le/teleportmirror/MirrorItem.java | 208 ++++++++++++++++++ .../com/le/teleportmirror/MirrorNetwork.java | 153 +++++++++++++ .../com/le/teleportmirror/MirrorTier.java | 28 +++ .../com/le/teleportmirror/MirrorType.java | 16 ++ .../le/teleportmirror/TeleportMirrorMod.java | 85 +++++++ .../screen/PlayerSelectionScreen.java | 100 +++++++++ .../assets/examplemod/lang/en_us.json | 5 - .../assets/teleportmirror/lang/en_us.json | 13 ++ .../assets/teleportmirror/lang/zh_cn.json | 13 ++ .../models/item/advanced_return_mirror.json | 6 + .../models/item/advanced_teleport_mirror.json | 6 + .../models/item/basic_return_mirror.json | 6 + .../models/item/basic_teleport_mirror.json | 6 + .../item/intermediate_return_mirror.json | 6 + .../item/intermediate_teleport_mirror.json | 6 + .../models/item/permanent_return_mirror.json | 6 + .../item/permanent_teleport_mirror.json | 6 + .../textures/item/advanced_return_mirror.png | Bin 0 -> 143 bytes .../item/advanced_teleport_mirror.png | Bin 0 -> 143 bytes .../textures/item/basic_return_mirror.png | Bin 0 -> 144 bytes .../textures/item/basic_teleport_mirror.png | Bin 0 -> 144 bytes .../item/intermediate_return_mirror.png | Bin 0 -> 144 bytes .../item/intermediate_teleport_mirror.png | Bin 0 -> 143 bytes .../textures/item/permanent_return_mirror.png | Bin 0 -> 144 bytes .../item/permanent_teleport_mirror.png | Bin 0 -> 144 bytes .../recipe/advanced_return_mirror.json | 19 ++ .../recipe/advanced_teleport_mirror.json | 19 ++ .../recipe/basic_return_mirror.json | 19 ++ .../recipe/basic_teleport_mirror.json | 19 ++ .../recipe/intermediate_return_mirror.json | 20 ++ .../recipe/intermediate_teleport_mirror.json | 20 ++ .../recipe/permanent_return_mirror.json | 19 ++ .../recipe/permanent_teleport_mirror.json | 19 ++ .../templates/META-INF/neoforge.mods.toml | 88 ++------ 38 files changed, 904 insertions(+), 261 deletions(-) delete mode 100644 src/main/java/com/example/examplemod/Config.java delete mode 100644 src/main/java/com/example/examplemod/ExampleMod.java create mode 100644 src/main/java/com/le/teleportmirror/Config.java create mode 100644 src/main/java/com/le/teleportmirror/MirrorItem.java create mode 100644 src/main/java/com/le/teleportmirror/MirrorNetwork.java create mode 100644 src/main/java/com/le/teleportmirror/MirrorTier.java create mode 100644 src/main/java/com/le/teleportmirror/MirrorType.java create mode 100644 src/main/java/com/le/teleportmirror/TeleportMirrorMod.java create mode 100644 src/main/java/com/le/teleportmirror/screen/PlayerSelectionScreen.java delete mode 100644 src/main/resources/assets/examplemod/lang/en_us.json create mode 100644 src/main/resources/assets/teleportmirror/lang/en_us.json create mode 100644 src/main/resources/assets/teleportmirror/lang/zh_cn.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/advanced_return_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/advanced_teleport_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/basic_return_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/basic_teleport_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/intermediate_return_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/intermediate_teleport_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/permanent_return_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/models/item/permanent_teleport_mirror.json create mode 100644 src/main/resources/assets/teleportmirror/textures/item/advanced_return_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/advanced_teleport_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/basic_return_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/basic_teleport_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/intermediate_return_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/intermediate_teleport_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/permanent_return_mirror.png create mode 100644 src/main/resources/assets/teleportmirror/textures/item/permanent_teleport_mirror.png create mode 100644 src/main/resources/data/teleportmirror/recipe/advanced_return_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/advanced_teleport_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/basic_return_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/basic_teleport_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/intermediate_return_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/intermediate_teleport_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/permanent_return_mirror.json create mode 100644 src/main/resources/data/teleportmirror/recipe/permanent_teleport_mirror.json diff --git a/gradle.properties b/gradle.properties index a0cde17..7d1e4d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,28 +12,28 @@ parchment_mappings_version=2024.11.10 # Environment Properties # You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge # The Minecraft version must agree with the Neo version to get a valid artifact -minecraft_version=1.21 +minecraft_version=1.21.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.21] +minecraft_version_range=[1.21.1] # The Neo version must agree with the Minecraft version to get a valid artifact -neo_version=21.0.167 +neo_version=21.1.227 # The loader version range can only use the major version of FML as bounds -loader_version_range=[1,) +loader_version_range=[2,) ## Mod Properties # 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=examplemod +mod_id=teleportmirror # The human-readable display name for the mod. -mod_name=Example Mod +mod_name=Teleport Mirror # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. -mod_license=All Rights Reserved +mod_license=MIT # The mod version. See https://semver.org/ mod_version=1.0.0 # 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=com.example.examplemod +mod_group_id=com.le.teleportmirror diff --git a/src/main/java/com/example/examplemod/Config.java b/src/main/java/com/example/examplemod/Config.java deleted file mode 100644 index 358706c..0000000 --- a/src/main/java/com/example/examplemod/Config.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.example.examplemod; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.event.config.ModConfigEvent; -import net.neoforged.neoforge.common.ModConfigSpec; - -// 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 Neo's config APIs -public class Config { - private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); - - public static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER - .comment("Whether to log the dirt block on common setup") - .define("logDirtBlock", true); - - public static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER - .comment("A magic number") - .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); - - public static final ModConfigSpec.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 - public static final ModConfigSpec.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 ModConfigSpec SPEC = BUILDER.build(); - - private static boolean validateItemName(final Object obj) { - return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(itemName)); - } -} diff --git a/src/main/java/com/example/examplemod/ExampleMod.java b/src/main/java/com/example/examplemod/ExampleMod.java deleted file mode 100644 index 34c7e73..0000000 --- a/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.example.examplemod; - -import org.slf4j.Logger; - -import com.mojang.logging.LogUtils; - -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.Component; -import net.minecraft.world.food.FoodProperties; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.CreativeModeTabs; -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.state.BlockBehaviour; -import net.minecraft.world.level.material.MapColor; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraft.client.Minecraft; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; -import net.neoforged.neoforge.event.server.ServerStartingEvent; -import net.neoforged.neoforge.registries.DeferredBlock; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredItem; -import net.neoforged.neoforge.registries.DeferredRegister; - -// The value here should match an entry in the META-INF/neoforge.mods.toml file -@Mod(ExampleMod.MODID) -public class ExampleMod { - // Define mod id in a common place for everything to reference - public static final String MODID = "examplemod"; - // Directly reference a slf4j logger - public static final Logger LOGGER = LogUtils.getLogger(); - // Create a Deferred Register to hold Blocks which will all be registered under the "examplemod" namespace - public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID); - // Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace - public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID); - // Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "examplemod" namespace - public static final DeferredRegister CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); - - // Creates a new Block with the id "examplemod:example_block", combining the namespace and path - public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock("example_block", BlockBehaviour.Properties.of().mapColor(MapColor.STONE)); - // Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path - public static final DeferredItem EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", EXAMPLE_BLOCK); - - // Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2 - public static final DeferredItem EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties().food(new FoodProperties.Builder() - .alwaysEdible().nutrition(1).saturationModifier(2f).build())); - - // Creates a creative tab with the id "examplemod:example_tab" for the example item, that is placed after the combat tab - public static final DeferredHolder EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder() - .title(Component.translatable("itemGroup.examplemod")) //The language key for the title of your CreativeModeTab - .withTabsBefore(CreativeModeTabs.COMBAT) - .icon(() -> EXAMPLE_ITEM.get().getDefaultInstance()) - .displayItems((parameters, output) -> { - output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event - }).build()); - - // The constructor for the mod class is the first code that is run when your mod is loaded. - // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. - public ExampleMod(IEventBus modEventBus, ModContainer modContainer) { - // Register the commonSetup method for modloading - modEventBus.addListener(this::commonSetup); - - // Register the Deferred Register to the mod event bus so blocks get registered - BLOCKS.register(modEventBus); - // Register the Deferred Register to the mod event bus so items get registered - ITEMS.register(modEventBus); - // Register the Deferred Register to the mod event bus so tabs get registered - CREATIVE_MODE_TABS.register(modEventBus); - - // Register ourselves for server and other game events we are interested in. - // Note that this is necessary if and only if we want *this* class (ExampleMod) to respond directly to events. - // Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below. - NeoForge.EVENT_BUS.register(this); - - // Register the item to a creative tab - modEventBus.addListener(this::addCreative); - - // Register our mod's ModConfigSpec so that FML can create and load the config file for us - modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); - } - - private void commonSetup(FMLCommonSetupEvent event) { - // Some common setup code - LOGGER.info("HELLO FROM COMMON SETUP"); - - if (Config.LOG_DIRT_BLOCK.getAsBoolean()) { - LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); - } - - LOGGER.info("{}{}", Config.MAGIC_NUMBER_INTRODUCTION.get(), Config.MAGIC_NUMBER.getAsInt()); - - Config.ITEM_STRINGS.get().forEach((item) -> LOGGER.info("ITEM >> {}", item)); - } - - // Add the example block item to the building blocks tab - private void addCreative(BuildCreativeModeTabContentsEvent event) { - if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) { - event.accept(EXAMPLE_BLOCK_ITEM); - } - } - - // You can use SubscribeEvent and let the Event Bus discover methods to call - @SubscribeEvent - public void onServerStarting(ServerStartingEvent event) { - // Do something when the server starts - LOGGER.info("HELLO from server starting"); - } - - // You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent - @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) - static class ClientModEvents { - @SubscribeEvent - static void onClientSetup(FMLClientSetupEvent event) { - // Some client setup code - LOGGER.info("HELLO FROM CLIENT SETUP"); - LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); - } - } -} diff --git a/src/main/java/com/le/teleportmirror/Config.java b/src/main/java/com/le/teleportmirror/Config.java new file mode 100644 index 0000000..165dbbb --- /dev/null +++ b/src/main/java/com/le/teleportmirror/Config.java @@ -0,0 +1,65 @@ +package com.le.teleportmirror; + +import net.neoforged.neoforge.common.ModConfigSpec; + +public class Config { + private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); + + public static final ModConfigSpec.IntValue COOLDOWN_SECONDS = BUILDER + .comment("Cooldown in seconds between mirror uses") + .defineInRange("cooldownSeconds", 60, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue CHARGE_TICKS = BUILDER + .comment("Number of ticks required to charge the mirror (20 ticks = 1 second)") + .defineInRange("chargeTicks", 40, 1, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue BASIC_DURABILITY = BUILDER + .comment("Durability for Basic tier mirrors") + .defineInRange("basicDurability", 10, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue INTERMEDIATE_DURABILITY = BUILDER + .comment("Durability for Intermediate tier mirrors") + .defineInRange("intermediateDurability", 50, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue ADVANCED_DURABILITY = BUILDER + .comment("Durability for Advanced tier mirrors") + .defineInRange("advancedDurability", 100, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue BASIC_NAUSEA_SECONDS = BUILDER + .comment("Nausea effect duration in seconds for Basic tier") + .defineInRange("basicNauseaSeconds", 10, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue BASIC_WITHER_SECONDS = BUILDER + .comment("Wither effect duration in seconds for Basic tier") + .defineInRange("basicWitherSeconds", 5, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue INTERMEDIATE_NAUSEA_SECONDS = BUILDER + .comment("Nausea effect duration in seconds for Intermediate tier") + .defineInRange("intermediateNauseaSeconds", 5, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue INTERMEDIATE_WITHER_SECONDS = BUILDER + .comment("Wither effect duration in seconds for Intermediate tier") + .defineInRange("intermediateWitherSeconds", 3, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.IntValue ADVANCED_NAUSEA_SECONDS = BUILDER + .comment("Nausea effect duration in seconds for Advanced tier") + .defineInRange("advancedNauseaSeconds", 3, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.BooleanValue ENABLE_BASIC_RECIPE = BUILDER + .comment("Enable crafting recipe for Basic tier mirrors") + .define("enableBasicRecipe", true); + + public static final ModConfigSpec.BooleanValue ENABLE_INTERMEDIATE_RECIPE = BUILDER + .comment("Enable crafting recipe for Intermediate tier mirrors") + .define("enableIntermediateRecipe", true); + + public static final ModConfigSpec.BooleanValue ENABLE_ADVANCED_RECIPE = BUILDER + .comment("Enable crafting recipe for Advanced tier mirrors") + .define("enableAdvancedRecipe", true); + + public static final ModConfigSpec.BooleanValue ENABLE_PERMANENT_RECIPE = BUILDER + .comment("Enable crafting recipe for Permanent tier mirrors") + .define("enablePermanentRecipe", true); + + static final ModConfigSpec SPEC = BUILDER.build(); +} diff --git a/src/main/java/com/le/teleportmirror/MirrorItem.java b/src/main/java/com/le/teleportmirror/MirrorItem.java new file mode 100644 index 0000000..869fece --- /dev/null +++ b/src/main/java/com/le/teleportmirror/MirrorItem.java @@ -0,0 +1,208 @@ +package com.le.teleportmirror; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.UseAnim; +import net.minecraft.world.level.Level; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.food.FoodData; + +public class MirrorItem extends Item { + private final MirrorTier tier; + private final MirrorType type; + + public MirrorItem(MirrorTier tier, MirrorType type, Properties properties) { + super(properties); + this.tier = tier; + this.type = type; + } + + public MirrorTier getTier() { + return tier; + } + + public MirrorType getMirrorType() { + return type; + } + + @Override + public UseAnim getUseAnimation(ItemStack stack) { + return UseAnim.BOW; + } + + @Override + public int getUseDuration(ItemStack stack, LivingEntity entity) { + return 72000; + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + if (player.getCooldowns().isOnCooldown(stack.getItem())) { + return InteractionResultHolder.fail(stack); + } + player.startUsingItem(hand); + return InteractionResultHolder.consume(stack); + } + + @Override + public void releaseUsing(ItemStack stack, Level level, LivingEntity entity, int timeCharged) { + if (!(entity instanceof Player player)) { + return; + } + + int chargeTicks = Config.CHARGE_TICKS.get(); + if (timeCharged < chargeTicks) { + return; + } + + if (level.isClientSide) { + return; + } + + ServerPlayer serverPlayer = (ServerPlayer) player; + + if (type == MirrorType.RETURN) { + performReturnTeleport(serverPlayer, stack); + } else { + MirrorNetwork.sendOpenSelectionToClient(serverPlayer, tier); + } + } + + private void performReturnTeleport(ServerPlayer player, ItemStack stack) { + ServerLevel serverLevel = player.serverLevel(); + BlockPos targetPos; + ServerLevel targetLevel; + ResourceKey respawnDimension = player.getRespawnDimension(); + BlockPos respawnPos = player.getRespawnPosition(); + + if (respawnPos != null) { + targetLevel = player.server.getLevel(respawnDimension); + if (targetLevel == null) { + targetLevel = serverLevel; + } + targetPos = respawnPos; + } else { + targetLevel = player.server.getLevel(Level.OVERWORLD); + if (targetLevel == null) { + targetLevel = serverLevel; + } + targetPos = targetLevel.getSharedSpawnPos(); + } + + consumeMirrorUse(player, stack); + + player.teleportTo(targetLevel, targetPos.getCenter().x, targetPos.getCenter().y, targetPos.getCenter().z, + player.getYRot(), player.getXRot()); + + targetLevel.playSound(null, targetPos, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0f, 1.0f); + } + + public void performTeleportToPlayer(ServerPlayer player, ServerPlayer target, ItemStack stack) { + ServerLevel targetLevel = target.serverLevel(); + BlockPos targetPos = target.blockPosition(); + + consumeMirrorUse(player, stack); + + player.teleportTo(targetLevel, targetPos.getCenter().x, targetPos.getCenter().y, targetPos.getCenter().z, + player.getYRot(), player.getXRot()); + + targetLevel.playSound(null, targetPos, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0f, 1.0f); + } + + private int getConfigMaxDamage() { + return switch (tier) { + case BASIC -> Config.BASIC_DURABILITY.get(); + case INTERMEDIATE -> Config.INTERMEDIATE_DURABILITY.get(); + case ADVANCED -> Config.ADVANCED_DURABILITY.get(); + case PERMANENT -> Integer.MAX_VALUE; + }; + } + + private void consumeMirrorUse(ServerPlayer player, ItemStack stack) { + applySideEffects(player); + applyCooldown(player); + + if (!tier.isPermanent()) { + int maxDamage = getConfigMaxDamage(); + if (stack.getDamageValue() + 1 >= maxDamage) { + stack.shrink(1); + player.level().playSound(null, player.blockPosition(), + SoundEvents.ITEM_BREAK, SoundSource.PLAYERS, 1.0f, 1.0f); + } else { + stack.setDamageValue(stack.getDamageValue() + 1); + } + } + } + + private void applyCooldown(Player player) { + int cooldownTicks = Config.COOLDOWN_SECONDS.get() * 20; + player.getCooldowns().addCooldown(this, cooldownTicks); + } + + private void applySideEffects(Player player) { + int nauseaDuration; + int witherDuration; + + switch (tier) { + case BASIC: + nauseaDuration = Config.BASIC_NAUSEA_SECONDS.get() * 20; + witherDuration = Config.BASIC_WITHER_SECONDS.get() * 20; + if (nauseaDuration > 0) { + player.addEffect(new MobEffectInstance(MobEffects.CONFUSION, nauseaDuration, 0)); + } + if (witherDuration > 0) { + player.addEffect(new MobEffectInstance(MobEffects.WITHER, witherDuration, 0)); + } + halveFood(player); + break; + case INTERMEDIATE: + nauseaDuration = Config.INTERMEDIATE_NAUSEA_SECONDS.get() * 20; + witherDuration = Config.INTERMEDIATE_WITHER_SECONDS.get() * 20; + if (nauseaDuration > 0) { + player.addEffect(new MobEffectInstance(MobEffects.CONFUSION, nauseaDuration, 0)); + } + if (witherDuration > 0) { + player.addEffect(new MobEffectInstance(MobEffects.WITHER, witherDuration, 0)); + } + halveFood(player); + break; + case ADVANCED: + nauseaDuration = Config.ADVANCED_NAUSEA_SECONDS.get() * 20; + if (nauseaDuration > 0) { + player.addEffect(new MobEffectInstance(MobEffects.CONFUSION, nauseaDuration, 0)); + } + halveFood(player); + break; + case PERMANENT: + break; + } + } + + private void halveFood(Player player) { + FoodData foodData = player.getFoodData(); + foodData.setFoodLevel(Math.max(1, foodData.getFoodLevel() / 2)); + foodData.setSaturation(0); + } + + @Override + public boolean isEnchantable(ItemStack stack) { + return false; + } + + @Override + public boolean isFoil(ItemStack stack) { + return tier == MirrorTier.PERMANENT; + } +} diff --git a/src/main/java/com/le/teleportmirror/MirrorNetwork.java b/src/main/java/com/le/teleportmirror/MirrorNetwork.java new file mode 100644 index 0000000..573829f --- /dev/null +++ b/src/main/java/com/le/teleportmirror/MirrorNetwork.java @@ -0,0 +1,153 @@ +package com.le.teleportmirror; + +import com.le.teleportmirror.screen.PlayerSelectionScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class MirrorNetwork { + + public static void registerPayloads(final RegisterPayloadHandlersEvent event) { + final PayloadRegistrar registrar = event.registrar(TeleportMirrorMod.MODID).versioned("1.0.0"); + + registrar.playToClient( + OpenPlayerSelectionPayload.TYPE, + OpenPlayerSelectionPayload.STREAM_CODEC, + MirrorNetwork::handleOpenSelection + ); + + registrar.playToServer( + RequestTeleportPayload.TYPE, + RequestTeleportPayload.STREAM_CODEC, + MirrorNetwork::handleRequestTeleport + ); + } + + public static void sendOpenSelectionToClient(ServerPlayer player, MirrorTier tier) { + List names = new ArrayList<>(); + List uuids = new ArrayList<>(); + + for (ServerPlayer onlinePlayer : player.server.getPlayerList().getPlayers()) { + if (!onlinePlayer.getUUID().equals(player.getUUID())) { + names.add(onlinePlayer.getName().getString()); + uuids.add(onlinePlayer.getUUID()); + } + } + + PacketDistributor.sendToPlayer(player, new OpenPlayerSelectionPayload(names, uuids, tier.getName())); + } + + private static void handleOpenSelection(OpenPlayerSelectionPayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + Minecraft client = Minecraft.getInstance(); + MirrorTier tier = MirrorTier.valueOf(payload.tierName.toUpperCase()); + client.setScreen(new PlayerSelectionScreen(payload.playerNames, payload.playerUuids, tier)); + }); + } + + private static void handleRequestTeleport(RequestTeleportPayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + ServerPlayer sender = (ServerPlayer) context.player(); + ServerPlayer target = sender.server.getPlayerList().getPlayer(payload.targetUuid); + + if (target == null) { + return; + } + + MirrorTier tier = MirrorTier.valueOf(payload.tierName.toUpperCase()); + ItemStack mainHand = sender.getMainHandItem(); + ItemStack offHand = sender.getOffhandItem(); + + ItemStack mirrorStack = null; + if (mainHand.getItem() instanceof MirrorItem mirrorItem && mirrorItem.getMirrorType() == MirrorType.TELEPORT) { + mirrorStack = mainHand; + } else if (offHand.getItem() instanceof MirrorItem mirrorItem && mirrorItem.getMirrorType() == MirrorType.TELEPORT) { + mirrorStack = offHand; + } + + if (mirrorStack == null || mirrorStack.isEmpty()) { + return; + } + + MirrorItem mirrorItem = (MirrorItem) mirrorStack.getItem(); + if (mirrorItem.getTier() != tier) { + return; + } + + mirrorItem.performTeleportToPlayer(sender, target, mirrorStack); + }); + } + + public record OpenPlayerSelectionPayload(List playerNames, List playerUuids, + String tierName) implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(TeleportMirrorMod.MODID, "open_player_selection")); + + public static final StreamCodec STREAM_CODEC = new StreamCodec<>() { + @Override + public OpenPlayerSelectionPayload decode(FriendlyByteBuf buf) { + int size = buf.readVarInt(); + List names = new ArrayList<>(); + List uuids = new ArrayList<>(); + for (int i = 0; i < size; i++) { + names.add(buf.readUtf()); + uuids.add(buf.readUUID()); + } + String tierName = buf.readUtf(); + return new OpenPlayerSelectionPayload(names, uuids, tierName); + } + + @Override + public void encode(FriendlyByteBuf buf, OpenPlayerSelectionPayload payload) { + buf.writeVarInt(payload.playerNames.size()); + for (int i = 0; i < payload.playerNames.size(); i++) { + buf.writeUtf(payload.playerNames.get(i)); + buf.writeUUID(payload.playerUuids.get(i)); + } + buf.writeUtf(payload.tierName); + } + }; + + @Override + public Type type() { + return TYPE; + } + } + + public record RequestTeleportPayload(UUID targetUuid, String tierName) implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(TeleportMirrorMod.MODID, "request_teleport")); + + public static final StreamCodec STREAM_CODEC = new StreamCodec<>() { + @Override + public RequestTeleportPayload decode(FriendlyByteBuf buf) { + UUID targetUuid = buf.readUUID(); + String tierName = buf.readUtf(); + return new RequestTeleportPayload(targetUuid, tierName); + } + + @Override + public void encode(FriendlyByteBuf buf, RequestTeleportPayload payload) { + buf.writeUUID(payload.targetUuid); + buf.writeUtf(payload.tierName); + } + }; + + @Override + public Type type() { + return TYPE; + } + } +} diff --git a/src/main/java/com/le/teleportmirror/MirrorTier.java b/src/main/java/com/le/teleportmirror/MirrorTier.java new file mode 100644 index 0000000..9e7458f --- /dev/null +++ b/src/main/java/com/le/teleportmirror/MirrorTier.java @@ -0,0 +1,28 @@ +package com.le.teleportmirror; + +public enum MirrorTier { + BASIC(10, "basic"), + INTERMEDIATE(50, "intermediate"), + ADVANCED(100, "advanced"), + PERMANENT(-1, "permanent"); + + private final int defaultDurability; + private final String name; + + MirrorTier(int defaultDurability, String name) { + this.defaultDurability = defaultDurability; + this.name = name; + } + + public int getDefaultDurability() { + return defaultDurability; + } + + public String getName() { + return name; + } + + public boolean isPermanent() { + return this == PERMANENT; + } +} diff --git a/src/main/java/com/le/teleportmirror/MirrorType.java b/src/main/java/com/le/teleportmirror/MirrorType.java new file mode 100644 index 0000000..24b6068 --- /dev/null +++ b/src/main/java/com/le/teleportmirror/MirrorType.java @@ -0,0 +1,16 @@ +package com.le.teleportmirror; + +public enum MirrorType { + RETURN("return"), + TELEPORT("teleport"); + + private final String name; + + MirrorType(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/le/teleportmirror/TeleportMirrorMod.java b/src/main/java/com/le/teleportmirror/TeleportMirrorMod.java new file mode 100644 index 0000000..257c422 --- /dev/null +++ b/src/main/java/com/le/teleportmirror/TeleportMirrorMod.java @@ -0,0 +1,85 @@ +package com.le.teleportmirror; + +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.CreativeModeTabs; +import net.minecraft.world.item.Item; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredItem; +import net.neoforged.neoforge.registries.DeferredRegister; + +@Mod(TeleportMirrorMod.MODID) +public class TeleportMirrorMod { + public static final String MODID = "teleportmirror"; + + public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID); + public static final DeferredRegister CREATIVE_MODE_TABS = + DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); + + public static final DeferredItem BASIC_RETURN_MIRROR = ITEMS.register("basic_return_mirror", + () -> new MirrorItem(MirrorTier.BASIC, MirrorType.RETURN, + new Item.Properties().stacksTo(1).durability(MirrorTier.BASIC.getDefaultDurability()))); + + public static final DeferredItem INTERMEDIATE_RETURN_MIRROR = ITEMS.register("intermediate_return_mirror", + () -> new MirrorItem(MirrorTier.INTERMEDIATE, MirrorType.RETURN, + new Item.Properties().stacksTo(1).durability(MirrorTier.INTERMEDIATE.getDefaultDurability()))); + + public static final DeferredItem ADVANCED_RETURN_MIRROR = ITEMS.register("advanced_return_mirror", + () -> new MirrorItem(MirrorTier.ADVANCED, MirrorType.RETURN, + new Item.Properties().stacksTo(1).durability(MirrorTier.ADVANCED.getDefaultDurability()))); + + public static final DeferredItem PERMANENT_RETURN_MIRROR = ITEMS.register("permanent_return_mirror", + () -> new MirrorItem(MirrorTier.PERMANENT, MirrorType.RETURN, + new Item.Properties().stacksTo(1))); + + public static final DeferredItem BASIC_TELEPORT_MIRROR = ITEMS.register("basic_teleport_mirror", + () -> new MirrorItem(MirrorTier.BASIC, MirrorType.TELEPORT, + new Item.Properties().stacksTo(1).durability(MirrorTier.BASIC.getDefaultDurability()))); + + public static final DeferredItem INTERMEDIATE_TELEPORT_MIRROR = ITEMS.register("intermediate_teleport_mirror", + () -> new MirrorItem(MirrorTier.INTERMEDIATE, MirrorType.TELEPORT, + new Item.Properties().stacksTo(1).durability(MirrorTier.INTERMEDIATE.getDefaultDurability()))); + + public static final DeferredItem ADVANCED_TELEPORT_MIRROR = ITEMS.register("advanced_teleport_mirror", + () -> new MirrorItem(MirrorTier.ADVANCED, MirrorType.TELEPORT, + new Item.Properties().stacksTo(1).durability(MirrorTier.ADVANCED.getDefaultDurability()))); + + public static final DeferredItem PERMANENT_TELEPORT_MIRROR = ITEMS.register("permanent_teleport_mirror", + () -> new MirrorItem(MirrorTier.PERMANENT, MirrorType.TELEPORT, + new Item.Properties().stacksTo(1))); + + public static final DeferredHolder MIRROR_TAB = + CREATIVE_MODE_TABS.register("mirror_tab", () -> CreativeModeTab.builder() + .title(Component.translatable("itemGroup.teleportmirror")) + .withTabsBefore(CreativeModeTabs.COMBAT) + .icon(() -> BASIC_RETURN_MIRROR.get().getDefaultInstance()) + .displayItems((parameters, output) -> { + output.accept(BASIC_RETURN_MIRROR.get()); + output.accept(INTERMEDIATE_RETURN_MIRROR.get()); + output.accept(ADVANCED_RETURN_MIRROR.get()); + output.accept(PERMANENT_RETURN_MIRROR.get()); + output.accept(BASIC_TELEPORT_MIRROR.get()); + output.accept(INTERMEDIATE_TELEPORT_MIRROR.get()); + output.accept(ADVANCED_TELEPORT_MIRROR.get()); + output.accept(PERMANENT_TELEPORT_MIRROR.get()); + }).build()); + + public TeleportMirrorMod(IEventBus modEventBus, ModContainer modContainer) { + ITEMS.register(modEventBus); + CREATIVE_MODE_TABS.register(modEventBus); + + modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); + modEventBus.addListener(this::onRegisterPayloads); + } + + private void onRegisterPayloads(final RegisterPayloadHandlersEvent event) { + MirrorNetwork.registerPayloads(event); + } +} diff --git a/src/main/java/com/le/teleportmirror/screen/PlayerSelectionScreen.java b/src/main/java/com/le/teleportmirror/screen/PlayerSelectionScreen.java new file mode 100644 index 0000000..b3ad732 --- /dev/null +++ b/src/main/java/com/le/teleportmirror/screen/PlayerSelectionScreen.java @@ -0,0 +1,100 @@ +package com.le.teleportmirror.screen; + +import com.le.teleportmirror.MirrorNetwork; +import com.le.teleportmirror.MirrorTier; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.neoforged.neoforge.network.PacketDistributor; + +import java.util.List; +import java.util.UUID; + +public class PlayerSelectionScreen extends Screen { + private final List playerNames; + private final List playerUuids; + private final MirrorTier tier; + private int scrollOffset = 0; + private static final int BUTTONS_PER_PAGE = 8; + + public PlayerSelectionScreen(List playerNames, List playerUuids, MirrorTier tier) { + super(Component.translatable("screen.teleportmirror.select_player")); + this.playerNames = playerNames; + this.playerUuids = playerUuids; + this.tier = tier; + } + + @Override + protected void init() { + super.init(); + rebuildButtons(); + } + + private void rebuildButtons() { + this.clearWidgets(); + + int centerX = this.width / 2; + int startY = 40; + int buttonWidth = 200; + int buttonHeight = 20; + int spacing = 4; + + int maxOffset = Math.max(0, playerNames.size() - BUTTONS_PER_PAGE); + if (scrollOffset > maxOffset) scrollOffset = maxOffset; + + int endIndex = Math.min(scrollOffset + BUTTONS_PER_PAGE, playerNames.size()); + for (int i = scrollOffset; i < endIndex; i++) { + final int idx = i; + int y = startY + (i - scrollOffset) * (buttonHeight + spacing); + this.addRenderableWidget(Button.builder( + Component.literal(playerNames.get(i)), + btn -> selectPlayer(idx)) + .pos(centerX - buttonWidth / 2, y) + .size(buttonWidth, buttonHeight) + .build()); + } + + if (playerNames.size() > BUTTONS_PER_PAGE) { + this.addRenderableWidget(Button.builder( + Component.literal("\u25B2"), + btn -> { scrollOffset = Math.max(0, scrollOffset - 1); rebuildButtons(); }) + .pos(centerX + buttonWidth / 2 + 5, startY) + .size(20, 20) + .build()); + this.addRenderableWidget(Button.builder( + Component.literal("\u25BC"), + btn -> { scrollOffset = Math.min(maxOffset, scrollOffset + 1); rebuildButtons(); }) + .pos(centerX + buttonWidth / 2 + 5, startY + BUTTONS_PER_PAGE * (buttonHeight + spacing) - spacing) + .size(20, 20) + .build()); + } + + this.addRenderableWidget(Button.builder( + Component.translatable("gui.cancel"), + btn -> this.onClose()) + .pos(centerX - 50, this.height - 30) + .size(100, 20) + .build()); + } + + private void selectPlayer(int index) { + if (index >= 0 && index < playerUuids.size()) { + UUID targetUuid = playerUuids.get(index); + PacketDistributor.sendToServer(new MirrorNetwork.RequestTeleportPayload(targetUuid, tier.getName())); + } + this.onClose(); + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + this.renderBackground(guiGraphics, mouseX, mouseY, partialTick); + guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 15, 0xFFFFFF); + super.render(guiGraphics, mouseX, mouseY, partialTick); + } + + @Override + public boolean isPauseScreen() { + return false; + } +} diff --git a/src/main/resources/assets/examplemod/lang/en_us.json b/src/main/resources/assets/examplemod/lang/en_us.json deleted file mode 100644 index 3b34651..0000000 --- a/src/main/resources/assets/examplemod/lang/en_us.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "itemGroup.examplemod": "Example Mod Tab", - "block.examplemod.example_block": "Example Block", - "item.examplemod.example_item": "Example Item" -} diff --git a/src/main/resources/assets/teleportmirror/lang/en_us.json b/src/main/resources/assets/teleportmirror/lang/en_us.json new file mode 100644 index 0000000..088b707 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/lang/en_us.json @@ -0,0 +1,13 @@ +{ + "itemGroup.teleportmirror": "Teleport Mirror", + "item.teleportmirror.basic_return_mirror": "Basic Return Mirror", + "item.teleportmirror.intermediate_return_mirror": "Intermediate Return Mirror", + "item.teleportmirror.advanced_return_mirror": "Advanced Return Mirror", + "item.teleportmirror.permanent_return_mirror": "Permanent Return Mirror", + "item.teleportmirror.basic_teleport_mirror": "Basic Teleport Mirror", + "item.teleportmirror.intermediate_teleport_mirror": "Intermediate Teleport Mirror", + "item.teleportmirror.advanced_teleport_mirror": "Advanced Teleport Mirror", + "item.teleportmirror.permanent_teleport_mirror": "Permanent Teleport Mirror", + "screen.teleportmirror.select_player": "Select Target Player", + "teleportmirror.cooldown": "Mirror is on cooldown" +} diff --git a/src/main/resources/assets/teleportmirror/lang/zh_cn.json b/src/main/resources/assets/teleportmirror/lang/zh_cn.json new file mode 100644 index 0000000..a66952c --- /dev/null +++ b/src/main/resources/assets/teleportmirror/lang/zh_cn.json @@ -0,0 +1,13 @@ +{ + "itemGroup.teleportmirror": "传送魔镜", + "item.teleportmirror.basic_return_mirror": "初级回城魔镜", + "item.teleportmirror.intermediate_return_mirror": "中级回城魔镜", + "item.teleportmirror.advanced_return_mirror": "高级回城魔镜", + "item.teleportmirror.permanent_return_mirror": "永久回城魔镜", + "item.teleportmirror.basic_teleport_mirror": "初级传送魔镜", + "item.teleportmirror.intermediate_teleport_mirror": "中级传送魔镜", + "item.teleportmirror.advanced_teleport_mirror": "高级传送魔镜", + "item.teleportmirror.permanent_teleport_mirror": "永久传送魔镜", + "screen.teleportmirror.select_player": "选择目标玩家", + "teleportmirror.cooldown": "魔镜冷却中" +} diff --git a/src/main/resources/assets/teleportmirror/models/item/advanced_return_mirror.json b/src/main/resources/assets/teleportmirror/models/item/advanced_return_mirror.json new file mode 100644 index 0000000..a39b765 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/advanced_return_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/advanced_return_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/advanced_teleport_mirror.json b/src/main/resources/assets/teleportmirror/models/item/advanced_teleport_mirror.json new file mode 100644 index 0000000..bf2d2d7 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/advanced_teleport_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/advanced_teleport_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/basic_return_mirror.json b/src/main/resources/assets/teleportmirror/models/item/basic_return_mirror.json new file mode 100644 index 0000000..98a80a5 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/basic_return_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/basic_return_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/basic_teleport_mirror.json b/src/main/resources/assets/teleportmirror/models/item/basic_teleport_mirror.json new file mode 100644 index 0000000..bd21114 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/basic_teleport_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/basic_teleport_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/intermediate_return_mirror.json b/src/main/resources/assets/teleportmirror/models/item/intermediate_return_mirror.json new file mode 100644 index 0000000..ed988a5 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/intermediate_return_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/intermediate_return_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/intermediate_teleport_mirror.json b/src/main/resources/assets/teleportmirror/models/item/intermediate_teleport_mirror.json new file mode 100644 index 0000000..e747e5d --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/intermediate_teleport_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/intermediate_teleport_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/permanent_return_mirror.json b/src/main/resources/assets/teleportmirror/models/item/permanent_return_mirror.json new file mode 100644 index 0000000..6d2004b --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/permanent_return_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/permanent_return_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/models/item/permanent_teleport_mirror.json b/src/main/resources/assets/teleportmirror/models/item/permanent_teleport_mirror.json new file mode 100644 index 0000000..a62fa54 --- /dev/null +++ b/src/main/resources/assets/teleportmirror/models/item/permanent_teleport_mirror.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "teleportmirror:item/permanent_teleport_mirror" + } +} diff --git a/src/main/resources/assets/teleportmirror/textures/item/advanced_return_mirror.png b/src/main/resources/assets/teleportmirror/textures/item/advanced_return_mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..66d54c8399b96c33506d1e558e0557ca56580c38 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK^0FI#}EtuJMPGkCiCxvXbP0l+XkK)Qu{P literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/teleportmirror/textures/item/basic_teleport_mirror.png b/src/main/resources/assets/teleportmirror/textures/item/basic_teleport_mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..a082c6c56295d4cf1890e9c0e3aae0400e33e88f GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK~+x|#}EtuFVdQ&MBb@0H?kvp8x;= literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/teleportmirror/textures/item/intermediate_teleport_mirror.png b/src/main/resources/assets/teleportmirror/textures/item/intermediate_teleport_mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..ae5b52d54d57bba1d2d2aa679369d1f5c3dd5d39 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK^0FI#}EtuFVdQ&MBb@09;Wg3jhEB literal 0 HcmV?d00001 diff --git a/src/main/resources/data/teleportmirror/recipe/advanced_return_mirror.json b/src/main/resources/data/teleportmirror/recipe/advanced_return_mirror.json new file mode 100644 index 0000000..ba43468 --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/advanced_return_mirror.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "DED", + "MRM", + "DBD" + ], + "key": { + "D": { "item": "minecraft:diamond" }, + "E": { "item": "minecraft:ender_eye" }, + "M": { "item": "minecraft:emerald" }, + "R": { "item": "teleportmirror:intermediate_return_mirror" }, + "B": { "item": "minecraft:blaze_rod" } + }, + "result": { + "id": "teleportmirror:advanced_return_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/advanced_teleport_mirror.json b/src/main/resources/data/teleportmirror/recipe/advanced_teleport_mirror.json new file mode 100644 index 0000000..c8151dc --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/advanced_teleport_mirror.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "DED", + "MRM", + "DBD" + ], + "key": { + "D": { "item": "minecraft:diamond" }, + "E": { "item": "minecraft:ender_eye" }, + "M": { "item": "minecraft:emerald" }, + "R": { "item": "teleportmirror:intermediate_teleport_mirror" }, + "B": { "item": "minecraft:blaze_rod" } + }, + "result": { + "id": "teleportmirror:advanced_teleport_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/basic_return_mirror.json b/src/main/resources/data/teleportmirror/recipe/basic_return_mirror.json new file mode 100644 index 0000000..4d28685 --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/basic_return_mirror.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "GCG", + "IBI", + "PIP" + ], + "key": { + "G": { "item": "minecraft:gold_ingot" }, + "C": { "item": "minecraft:compass" }, + "B": { "item": "minecraft:blue_stained_glass_pane" }, + "I": { "item": "minecraft:iron_ingot" }, + "P": { "item": "minecraft:copper_ingot" } + }, + "result": { + "id": "teleportmirror:basic_return_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/basic_teleport_mirror.json b/src/main/resources/data/teleportmirror/recipe/basic_teleport_mirror.json new file mode 100644 index 0000000..5f68450 --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/basic_teleport_mirror.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "GLG", + "IOI", + "PIP" + ], + "key": { + "G": { "item": "minecraft:gold_ingot" }, + "L": { "item": "minecraft:lead" }, + "O": { "item": "minecraft:orange_stained_glass_pane" }, + "I": { "item": "minecraft:iron_ingot" }, + "P": { "item": "minecraft:copper_ingot" } + }, + "result": { + "id": "teleportmirror:basic_teleport_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/intermediate_return_mirror.json b/src/main/resources/data/teleportmirror/recipe/intermediate_return_mirror.json new file mode 100644 index 0000000..fd77cda --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/intermediate_return_mirror.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "DED", + "LRL", + "TAT" + ], + "key": { + "D": { "item": "minecraft:diamond" }, + "E": { "item": "minecraft:ender_pearl" }, + "L": { "item": "minecraft:lapis_lazuli" }, + "R": { "item": "teleportmirror:basic_return_mirror" }, + "T": { "item": "minecraft:redstone" }, + "A": { "item": "minecraft:amethyst_shard" } + }, + "result": { + "id": "teleportmirror:intermediate_return_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/intermediate_teleport_mirror.json b/src/main/resources/data/teleportmirror/recipe/intermediate_teleport_mirror.json new file mode 100644 index 0000000..6661cea --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/intermediate_teleport_mirror.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "DED", + "LRL", + "TAT" + ], + "key": { + "D": { "item": "minecraft:diamond" }, + "E": { "item": "minecraft:ender_pearl" }, + "L": { "item": "minecraft:lapis_lazuli" }, + "R": { "item": "teleportmirror:basic_teleport_mirror" }, + "T": { "item": "minecraft:redstone" }, + "A": { "item": "minecraft:amethyst_shard" } + }, + "result": { + "id": "teleportmirror:intermediate_teleport_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/permanent_return_mirror.json b/src/main/resources/data/teleportmirror/recipe/permanent_return_mirror.json new file mode 100644 index 0000000..152c31e --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/permanent_return_mirror.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SCS", + "ERE", + "SBS" + ], + "key": { + "S": { "item": "minecraft:shulker_shell" }, + "C": { "item": "minecraft:chorus_fruit" }, + "E": { "item": "minecraft:ender_pearl" }, + "R": { "item": "teleportmirror:advanced_return_mirror" }, + "B": { "item": "minecraft:blaze_rod" } + }, + "result": { + "id": "teleportmirror:permanent_return_mirror", + "count": 1 + } +} diff --git a/src/main/resources/data/teleportmirror/recipe/permanent_teleport_mirror.json b/src/main/resources/data/teleportmirror/recipe/permanent_teleport_mirror.json new file mode 100644 index 0000000..c84329d --- /dev/null +++ b/src/main/resources/data/teleportmirror/recipe/permanent_teleport_mirror.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SCS", + "ERE", + "SBS" + ], + "key": { + "S": { "item": "minecraft:shulker_shell" }, + "C": { "item": "minecraft:chorus_fruit" }, + "E": { "item": "minecraft:ender_pearl" }, + "R": { "item": "teleportmirror:advanced_teleport_mirror" }, + "B": { "item": "minecraft:blaze_rod" } + }, + "result": { + "id": "teleportmirror:permanent_teleport_mirror", + "count": 1 + } +} diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml index cc5e2d2..f47e9ec 100644 --- a/src/main/templates/META-INF/neoforge.mods.toml +++ b/src/main/templates/META-INF/neoforge.mods.toml @@ -1,95 +1,33 @@ -# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods. -# There are several mandatory fields (#mandatory), and many more that are optional (#optional). -# The overall format is standard TOML format, v0.5.0. -# Note that there are a couple of TOML lists in this file. -# Find more information on toml format here: https://github.com/toml-lang/toml +modLoader="javafml" -# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml -modLoader="javafml" #mandatory +loaderVersion="${loader_version_range}" -# A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2. -loaderVersion="${loader_version_range}" #mandatory - -# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. -# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. license="${mod_license}" -# A URL to refer people to when problems occur with this mod -#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +[[mods]] -# A list of mods - how many allowed here is determined by the individual mod loader -[[mods]] #mandatory +modId="${mod_id}" -# The modid of the mod -modId="${mod_id}" #mandatory +version="${mod_version}" -# The version number of the mod -version="${mod_version}" #mandatory +displayName="${mod_name}" -# A display name for the mod -displayName="${mod_name}" #mandatory - -# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ -#updateJSONURL="https://change.me.example.invalid/updates.json" #optional - -# A URL for the "homepage" for this mod, displayed in the mod UI -#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional - -# A file name (in the root of the mod JAR) containing a logo for display -#logoFile="examplemod.png" #optional - -# A text field displayed in the mod UI -#credits="" #optional - -# The authors of the mod, displayed in the mod UI (optional) -#authors="" - -# The description text for the mod (multi line!) (#mandatory) description=''' -Example mod description. +Teleport Mirror mod adds magical mirrors that allow players to teleport. +Two types: Return Mirror (teleports to spawn) and Teleport Mirror (teleports to another player). +Four tiers: Basic, Intermediate, Advanced, and Permanent. ''' -# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. -#[[mixins]] -#config="${mod_id}.mixins.json" - -# The [[accessTransformers]] block allows you to declare where your AT file is. -# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg -#[[accessTransformers]] -#file="META-INF/accesstransformer.cfg" - -# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json - -# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. -[[dependencies.${mod_id}]] #optional - # the modid of the dependency - modId="neoforge" #mandatory - # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). - # 'required' requires the mod to exist, 'optional' does not - # 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning - type="required" #mandatory - # Optional field describing why the dependency is required or why it is incompatible - # reason="..." - # The version range of the dependency - versionRange="[${neo_version},)" #mandatory - # An ordering relationship for the dependency. - # BEFORE - This mod is loaded BEFORE the dependency - # AFTER - This mod is loaded AFTER the dependency +[[dependencies.${mod_id}]] + modId="neoforge" + type="required" + versionRange="[${neo_version},)" ordering="NONE" - # Side this dependency is applied on - BOTH, CLIENT, or SERVER side="BOTH" -# Here's another dependency [[dependencies.${mod_id}]] modId="minecraft" type="required" - # This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" - -# Features are specific properties of the game environment, that you may want to declare you require. This example declares -# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't -# stop your mod loading on the server for example. -#[features.${mod_id}] -#openGLVersion="[3.2,)"