Feature は砂漠の建物みたいなやつとか樹氷とかボーナスチェストのことです。
Feature はいろいろありますが今回は比較的簡単な構造をしているボーナスチェストをもとに作りました。
LootTableはチェストの中身を決めるものです。
実際は ResourceLocation の一種ですが、レシピなどと違って自由がきかないみたいです。
もし反映させる方法などあったら教えてほしいです。
ただチェストの LootTable に関しては 1.15.2 ではどうやら追加するのが難しいらしいのであきらめました。
1.14 を扱っている人のコードでは追加できていたのでそれをまねして書いたのですがうまくいきませんでした。
ということで諦めて既存のチェストの中身を使っていろいろやることにしました。
Feature の生成はバイオームを限定して作ることもできますが、今回は全部のバイオームに追加しました。
まずは Feature の本体です。
チェストの周りに4つチェストが生成されるようになってます。
それぞれのチェストの中身は一応ランダムです。
周りに生成されるチェストの中身は50%の確率でゴミになるようにしてます。
for文の前にif(rand.nextInt(number))とか入れると生成される数を減らすことができます。
ゴミ箱生成率を変えたい場合はRandomResourceの数字をいじってください。
実際に適応した動画
DropChestFeature.java
import com.mojang.datafixers.Dynamic;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.material.Material;
import net.minecraft.tileentity.LockableLootTileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import work.prgrm.battlemod.loots.ModLootTables;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class DropChestFeature extends Feature {
public DropChestFeature(Function<Dynamic<?>, ? extends NoFeatureConfig> conf){
super(conf);
}
@Override
public boolean place(@Nonnull IWorld worldIn,@Nonnull ChunkGenerator<? extends GenerationSettings> generator,@Nonnull Random rand,@Nonnull BlockPos pos,@Nonnull NoFeatureConfig config){
ChunkPos chunkpos = new ChunkPos(pos);
List list = IntStream.rangeClosed(chunkpos.getXStart(), chunkpos.getXEnd()).boxed().collect(Collectors.toList());
Collections.shuffle(list, rand);
List list1 = IntStream.rangeClosed(chunkpos.getZStart(), chunkpos.getZEnd()).boxed().collect(Collectors.toList());
Collections.shuffle(list1, rand);
BlockPos.Mutable blockpos$mutable = new BlockPos.Mutable();
for(Integer integer : list) {
for(Integer integer1 : list1) {
blockpos$mutable.setPos(integer, 0, integer1);
BlockPos blockpos = worldIn.getHeight(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, blockpos$mutable);
if (worldIn.isAirBlock(blockpos) || worldIn.getBlockState(blockpos).getCollisionShape(worldIn, blockpos).isEmpty() && !(worldIn.getBlockState(blockpos.down()).getMaterial() == Material.WATER)) {
worldIn.setBlockState(blockpos, Blocks.CHEST.getDefaultState(), 2);
LockableLootTileEntity.setLootTable(worldIn, rand, blockpos, ModLootTables.RandomResource(36));
BlockState blockstate = Blocks.CHEST.getDefaultState();
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos blockpos1 = blockpos.offset(direction);
if (blockstate.isValidPosition(worldIn, blockpos1)) {
worldIn.setBlockState(blockpos1, blockstate, 2);
LockableLootTileEntity.setLootTable(worldIn, rand, blockpos1, ModLootTables.RandomResource(72));
}
}
return true;
}
}
}
return false;
}
}
次はチェストの中身を決めるクラスです。
中身は全部で35種類あります。詳しくはそれぞれのjsonファイルの中身を見るとわかると思います。
ModLootTables.java
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.LootTables;
import java.util.*;
public class ModLootTables {
public static final ResourceLocation DROP_CHEST = new ResourceLocation(MyMod.MOD_ID, "chests/dropchest");
public static ResourceLocation RandomResource(int num)
{
Random rand = new Random();
ResourceLocation res;
switch (rand.nextInt(num)){
case 1:res = LootTables.CHESTS_VILLAGE_VILLAGE_PLAINS_HOUSE;break;
case 2:res = LootTables.CHESTS_VILLAGE_VILLAGE_WEAPONSMITH;break;
case 3:res = LootTables.CHESTS_VILLAGE_VILLAGE_TOOLSMITH;break;
case 4:res = LootTables.CHESTS_VILLAGE_VILLAGE_TEMPLE;break;
case 5:res = LootTables.CHESTS_VILLAGE_VILLAGE_TANNERY;break;
case 6:res = LootTables.CHESTS_VILLAGE_VILLAGE_TAIGA_HOUSE;break;
case 7:res = LootTables.CHESTS_VILLAGE_VILLAGE_SNOWY_HOUSE;break;
case 8:res = LootTables.CHESTS_VILLAGE_VILLAGE_SHEPHERD;break;
case 9:res = LootTables.CHESTS_VILLAGE_VILLAGE_BUTCHER;break;
case 10:res = LootTables.CHESTS_VILLAGE_VILLAGE_FLETCHER;break;
case 11:res = LootTables.CHESTS_VILLAGE_VILLAGE_SAVANNA_HOUSE;break;
case 12:res = LootTables.CHESTS_SPAWN_BONUS_CHEST;break;
case 13:res = LootTables.CHESTS_VILLAGE_VILLAGE_FISHER;break;
case 14:res = LootTables.CHESTS_VILLAGE_VILLAGE_DESERT_HOUSE;break;
case 15:res = LootTables.CHESTS_VILLAGE_VILLAGE_CARTOGRAPHER;break;
case 16:res = LootTables.CHESTS_VILLAGE_VILLAGE_ARMORER;break;
case 17:res = LootTables.CHESTS_UNDERWATER_RUIN_SMALL;break;
case 18:res = LootTables.CHESTS_UNDERWATER_RUIN_BIG;break;
case 19:res = LootTables.CHESTS_STRONGHOLD_LIBRARY;break;
case 20:res = LootTables.CHESTS_STRONGHOLD_CROSSING;break;
case 21:res = LootTables.CHESTS_STRONGHOLD_CORRIDOR;break;
case 22:res = LootTables.CHESTS_SIMPLE_DUNGEON;break;
case 23:res = LootTables.CHESTS_SHIPWRECK_TREASURE;break;
case 24:res = LootTables.CHESTS_SHIPWRECK_SUPPLY;break;
case 25:res = LootTables.CHESTS_SHIPWRECK_MAP;break;
case 26:res = LootTables.CHESTS_PILLAGER_OUTPOST;break;
case 27:res = LootTables.CHESTS_NETHER_BRIDGE;break;
case 28:res = LootTables.CHESTS_JUNGLE_TEMPLE_DISPENSER;break;
case 29:res = LootTables.CHESTS_IGLOO_CHEST;break;
case 30:res = LootTables.CHESTS_END_CITY_TREASURE;break;
case 31:res = LootTables.CHESTS_DESERT_PYRAMID;break;
case 32:res = LootTables.CHESTS_BURIED_TREASURE;break;
case 33:res = LootTables.CHESTS_ABANDONED_MINESHAFT;break;
case 34:res = LootTables.CHESTS_WOODLAND_MANSION;break;
default:res = LootTables.CHESTS_VILLAGE_VILLAGE_MASON;break;
}
return res;
}
}
次にFeatureを登録します。
ModFeatures.java
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(modid = MyMod.MOD_ID,bus = Mod.EventBusSubscriber.Bus.MOD)
public class ModFeatures {
public static final DropChestFeature DROP_CHEST = new DropChestFeature(NoFeatureConfig::deserialize);
@SubscribeEvent
public static void onFeatureRegister(final RegistryEvent.Register<Feature<?>> event){
DROP_CHEST.setRegistryName("drop_chest");
event.getRegistry().register(DROP_CHEST);
}
}
次にそれぞれのバイオームに追加するためのクラスを作ります。
WorldGenManager.java
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.placement.ConfiguredPlacement;
import net.minecraft.world.gen.placement.FrequencyConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.registries.ForgeRegistries;
public class WorldGenManager {
public WorldGenManager(){}
public static void setup(){
for(Biome biome: ForgeRegistries.BIOMES){
biome.addFeature(GenerationStage.Decoration.TOP_LAYER_MODIFICATION,
ModFeatures.DROP_CHEST
.withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG)
.withPlacement(Placement.COUNT_TOP_SOLID
.configure(new FrequencyConfig(1)))
);
}
}
}
MyMod.java
最後に上のクラスの関数を実行して終わりです。
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import work.prgrm.battlemod.features.DropChestFeature;
import work.prgrm.battlemod.init.WorldGenManager;
@Mod(MyMod.MOD_ID)
public class MyMod {
public static final String MOD_ID ="mymod";
public Battle(){
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
}
private void setup(final FMLCommonSetupEvent event){
WorldGenManager.setup();
}
}
以上です。
バイオームに登録するところに関してはこちらの記事を参考にさせてもらいました。
https://json-fileman.hatenablog.com/entry/2020/03/17/011641