- 回城魔镜:传送至出生点(优先床/重生锚,否则世界出生点) - 传送魔镜:打开玩家选择UI后传送到目标玩家身边 - 4个等级(初级/中级/高级/永久),支持耐久、冷却、副作用配置 - 8个合成配方 - 支持中英文语言 - 修复配置加载顺序问题
@@ -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<String> 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<List<? extends String>> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<CreativeModeTab> 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<Block> 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<BlockItem> 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<Item> 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<CreativeModeTab, CreativeModeTab> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/main/java/com/le/teleportmirror/Config.java
Normal file
@@ -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();
|
||||
}
|
||||
208
src/main/java/com/le/teleportmirror/MirrorItem.java
Normal file
@@ -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<ItemStack> 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<Level> 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;
|
||||
}
|
||||
}
|
||||
153
src/main/java/com/le/teleportmirror/MirrorNetwork.java
Normal file
@@ -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<String> names = new ArrayList<>();
|
||||
List<UUID> 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<String> playerNames, List<UUID> playerUuids,
|
||||
String tierName) implements CustomPacketPayload {
|
||||
public static final Type<OpenPlayerSelectionPayload> TYPE = new Type<>(
|
||||
ResourceLocation.fromNamespaceAndPath(TeleportMirrorMod.MODID, "open_player_selection"));
|
||||
|
||||
public static final StreamCodec<FriendlyByteBuf, OpenPlayerSelectionPayload> STREAM_CODEC = new StreamCodec<>() {
|
||||
@Override
|
||||
public OpenPlayerSelectionPayload decode(FriendlyByteBuf buf) {
|
||||
int size = buf.readVarInt();
|
||||
List<String> names = new ArrayList<>();
|
||||
List<UUID> 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<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record RequestTeleportPayload(UUID targetUuid, String tierName) implements CustomPacketPayload {
|
||||
public static final Type<RequestTeleportPayload> TYPE = new Type<>(
|
||||
ResourceLocation.fromNamespaceAndPath(TeleportMirrorMod.MODID, "request_teleport"));
|
||||
|
||||
public static final StreamCodec<FriendlyByteBuf, RequestTeleportPayload> 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<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/le/teleportmirror/MirrorTier.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
16
src/main/java/com/le/teleportmirror/MirrorType.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
85
src/main/java/com/le/teleportmirror/TeleportMirrorMod.java
Normal file
@@ -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<CreativeModeTab> CREATIVE_MODE_TABS =
|
||||
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);
|
||||
|
||||
public static final DeferredItem<MirrorItem> 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<MirrorItem> 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<MirrorItem> 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<MirrorItem> PERMANENT_RETURN_MIRROR = ITEMS.register("permanent_return_mirror",
|
||||
() -> new MirrorItem(MirrorTier.PERMANENT, MirrorType.RETURN,
|
||||
new Item.Properties().stacksTo(1)));
|
||||
|
||||
public static final DeferredItem<MirrorItem> 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<MirrorItem> 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<MirrorItem> 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<MirrorItem> PERMANENT_TELEPORT_MIRROR = ITEMS.register("permanent_teleport_mirror",
|
||||
() -> new MirrorItem(MirrorTier.PERMANENT, MirrorType.TELEPORT,
|
||||
new Item.Properties().stacksTo(1)));
|
||||
|
||||
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<String> playerNames;
|
||||
private final List<UUID> playerUuids;
|
||||
private final MirrorTier tier;
|
||||
private int scrollOffset = 0;
|
||||
private static final int BUTTONS_PER_PAGE = 8;
|
||||
|
||||
public PlayerSelectionScreen(List<String> playerNames, List<UUID> 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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"itemGroup.examplemod": "Example Mod Tab",
|
||||
"block.examplemod.example_block": "Example Block",
|
||||
"item.examplemod.example_item": "Example Item"
|
||||
}
|
||||
13
src/main/resources/assets/teleportmirror/lang/en_us.json
Normal file
@@ -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"
|
||||
}
|
||||
13
src/main/resources/assets/teleportmirror/lang/zh_cn.json
Normal file
@@ -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": "魔镜冷却中"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/advanced_return_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/advanced_teleport_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/basic_return_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/basic_teleport_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/intermediate_return_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/intermediate_teleport_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/permanent_return_mirror"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "teleportmirror:item/permanent_teleport_mirror"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 144 B |
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,)"
|
||||
|
||||