本期我们将继续讲解Minecraft Forge Mod的开发,重点是Forge的配置文件。最后我们会补充上期教程中漏掉的Capability的一个关键点。
在此之前,有人询问事件注册为何没有生效。你可以参考这张图,左边是事件注册的方法,右边是对应的写法。如果你使用了@SubscribeEvent这样的方法,就需要将注册的方法定义为static void。另外,如果通过@Mod.EventBusSubscriber注册事件,在Register内填入实例或方法引用时,注册的方法就不能是static。
接着我们来讲Forge的配置文件。首先新建一个名为CONFIG的包,然后创建一个Java类,例如命名为CommonConfig。在这个类中,首先要定义一个公共的构建工具,比如private static ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();,然后考虑要定义哪些变量。假设之前我们写的是固定数值,每次升级增加或扣除一个经验点,而我们想要自定义这个数值,可以声明一个整数类型的经验值变量,类似:
private static ForgeConfigSpec.IntValue xpIncrease = builder.comment("Experience points increase").defineInRange("xpIncrease", 1, 0, Integer.MAX_VALUE);。
我们也想要自定义扣除多少经验,所以可以声明一个xpDecrease变量,使用
下列代码来定义扣除的经验值范围。
builder.defineInRange("xpDecrease", 1, 0, Integer.MAX_VALUE);
接下来,我们需要定义一个列表。在这个列表中,我们不希望固定规则对所有方块应用相同的经验值操作,而是希望能够自由地决定哪些方块增加经验值,哪些方块减少经验值。为此,我们需要定义一个列表变量:
private static ForgeConfigSpec.ConfigValue<List<? extends Block>> blocksGetXp = builder.comment("Blocks that give experience points").defineList("blocksGetXp", Collections.singletonList(Blocks.CARROTS), obj -> ForgeRegistries.BLOCKS.containsKey((ResourceLocation)obj.toString()), "Validation method for block entries");
在这里,我们首先将对象转换为Stream,然后将其作为字符串验证是否存在这个方块。我们需要编写一个验证方法:
private static boolean allow(Object obj) { return ForgeRegistries.BLOCKS.containsKey(new ResourceLocation(obj.toString())); }
最后,将该验证方法传递给defineList方法即可。
此外,如果忘记了方块的ID,可以在原版注册链中查找。例如,"canvas potatoes" 应为 "carved pumpkins"。
最后我们只需构建这个配置即可,使用public static ForgeConfigSpec spec = builder.build(); 完成构建,这样就创建好了。
另外有一个关键步骤是将这个配置注册到我们的Mod中。在构造函数中,可以通过modLoadingContext.get().registerConfig来实现。第一个参数是类型,有三种类型:CLINET、COMMON和SERVER。CLINET和COMMON会在客户端和服务器端都生效,而SERVER则会在创建的存档中有一个专门的配置文件。通常我们可以先使用COMMON类型,第二个参数就是之前创建的配置spec 对象,如CommonConfig.builder.
注册完成后,我们需要处理这些参数。可以先将获取到的列表转换成块对象以便后续处理。例如,声明一个私有静态方法getConfig(),在其中定义一些变量,如public static int xpIncrease; public static int xpDecrease;,然后转换这两个列表为 Set 类型。由于这些配置在整个游戏中都是唯一的并且不会改变,因此可以将它们设置为静态变量。
接下来我们使用一个 Map 对象,可以让我们对这些内容进行自定义转换。首先是将方块名称转换为对应的方块对象,在getBlockName方法中,我们使用ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName))来获取方块对象,最后将其转换为 Set 并存储在blockSet中。
接着在getConfig方法中执行get操作,用于初始化配置内容。我们可以在适当的时机触发这些操作,比如在事件总线上监听特定事件。例如,可以在客户端完全加载后执行onSetup方法,通过监听FMLClientSetupEvent事件,调用getConfig()方法。另外,也可以在重新加载配置时触发处理,通过监听ModConfigEvent.Reloading事件,并再次调用getConfig()。
然后,我们需要根据配置来应用经验值的增加和减少。可以通过CommonConfig.xpDecrease和CommonConfig.xpIncrease来获取对应的数值。修改判断条件,将名字改为useXp,并检查方块列表中是否包含当前方块,以决定是否使用经验。
进一步完善,考虑到不希望在作物已成熟时使用骨粉扣除经验,我们可以添加额外的判断条件。如果方块是可生长的方块并且目标方块不是泥土方块或海绵方块,同时目标方块是有效的目标方块,我们就可以从事件中获取级别和位置等信息,并将其传入判断方法中。如果条件满足,则将相关操作应用在对应的方块上。
对于涨经验的操作,同样可以按照类似的逻辑进行处理。我们可以仿照扣除经验的部分,根据配置涨经验的数值并更新对应的方块。
只需调整一些地方,主要是关于blockState的处理。在判断中,将block.getState()改为event.getState().getBlock(),这样就能正确获取到方块对象了。
接着,在对方块进行经验值处理时,我们可以根据配置中定义的方块和经验值来更新对应的逻辑。如果包含在列表中,我们可以直接复制之前的处理逻辑,然后稍作修改以符合新的要求。
回到主方法,添加一个static修饰符,并对条件进行反转处理。对于已成熟的作物,我们希望它不会消耗经验值,因此需要对判断条件进行相应的修改。
运行测试后,我们可以观察到在配置文件中设置的默认值和对应的作物列表。通过测试,可以验证使用骨粉对土豆的影响情况,包括涨经验、使用次数等。同时,也可以尝试对其他作物进行类似的测试,以验证逻辑是否生效。
若需修改配置文件,可以直接在配置界面进行更改。比如将两种作物都设置为消耗经验值,然后测试小麦等作物的表现。根据实际需求,可以调整经验值增加的数量,并观察不同作物的表现。
总的来说,这个逻辑并不复杂,主要是对各种情况进行判断和处理。对于自定义修改或完善逻辑,你可以根据具体需求来对代码进行适当的调整。
import net.minecraftforge.event.entity.player.PlayerEvent;import net.minecraftforge.eventbus.api.SubscribeEvent;import net.minecraftforge.fml.common.Mod;@Mod.EventBusSubscriber(modid ="your_mod_id")publicclassPlayerCloneEventListener{@SubscribeEventpublicstaticvoidonPlayerClone(PlayerEvent.Cloneevent){if(event.isWasDeath()) {PlayerEntity originalPlayer =event.getOriginal();PlayerEntity newPlayer =event.getPlayer();// 复制经验值if(originalPlayer.experienceLevel >0) {newPlayer.addExperienceLevel(originalPlayer.experienceLevel - newPlayer.experienceLevel);}// 复制其他Capability,这里以经验值Capability为例YourCapability oldCap = originalPlayer.getCapability(YourCapability.CAPABILITY).orElse(null);YourCapability newCap = newPlayer.getCapability(YourCapability.CAPABILITY).orElse(null);if(oldCap !=null&& newCap !=null) {newCap.setXP(oldCap.getXP());}}}}
这段代码主要处理了在玩家死亡时经验值丢失的问题,以及如何保留Capability在死亡事件中不重置。在PlayerCloneEvent事件监听器中,我们需要复制原始玩家的经验值到新的玩家对象中,以确保在死亡后不会丢失经验值。
首先,在PlayerCloneEvent事件监听方法中,我们通过获取原始玩家对象和当前玩家对象,并将原始玩家的经验值复制给新的玩家对象。这样可以保证在复制过程中经验值得以保存。
接着,我们需要进行一些额外的操作,比如获取并设置经验值,确保正确复制老玩家的经验值。最后,清理多余的代码,使整个逻辑更加简洁明了。
通过这样的处理,就能够解决在玩家死亡时经验值丢失的问题,并确保Capability在死亡事件中得以保留不重置。这种方式相对简单有效,只需添加一个事件监听器即可实现所需功能。