MOD製作チュートリアル > 村人の追加

概要

村人の職業を追加し、専用の家を村に生成させる。

ソースコード

  • AluminiumMod.java
package tutorial.aluminiummod;
 
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.VillagerRegistry;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.gen.structure.MapGenStructureIO;
 
@Mod(modid = "AluminiumMod", name = "Aluminium Mod", version = "1.0.0")
public class AluminiumMod {
 
    /**
     * 村人IDの宣言
     * 0~4は既存の村人として設定されているため、使ってはいけない
     */
    public static final int metalVillagerID = 334;
    //追加するアイテム・ブロックの宣言
    public static Item aluminium;
    public static Block oreAluminium;
 
    //村人の宣言
 
    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        //アイテム・ブロックの実装
        aluminium = new Item()
                .setCreativeTab(CreativeTabs.tabMaterials)
                .setUnlocalizedName("aluminium")
                .setTextureName("aluminiummod:aluminium");
        GameRegistry.registerItem(aluminium, "aluminium");
 
        oreAluminium = new AluminiumOre()
                .setBlockName("oreAluminium")
                .setBlockTextureName("aluminiummod:aluminium_ore");
        GameRegistry.registerBlock(oreAluminium, "oreAluminium");
    }
 
    @Mod.EventHandler
    public void init(FMLInitializationEvent event) {
        //村人IDの登録
        VillagerRegistry.instance().registerVillagerId(metalVillagerID);
        //村人の登録
        VillagerRegistry.instance().registerVillageTradeHandler(metalVillagerID, new AluminiumTrade());
        if (FMLCommonHandler.instance().getSide().isClient()) {
            VillagerRegistry.instance().registerVillagerSkin(metalVillagerID,
                    new ResourceLocation("aluminiummod:textures/mobs/metal_villager.png"));
        }
        //村人生成の登録
        VillagerRegistry.instance().registerVillageCreationHandler(new AluminiumVillageCreation());
 
        //構造物の登録
        MapGenStructureIO.registerStructure(AluminiumHouse.class, "AluminiumHouse");
        MapGenStructureIO.func_143031_a(AluminiumHouse.class, "AluminiumHouse_");
    }
}
 
 
  • AluminiumOre.java

  • AluminiumTrade.java
package tutorial.aluminiummod;
 
import cpw.mods.fml.common.registry.VillagerRegistry;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.village.MerchantRecipe;
import net.minecraft.village.MerchantRecipeList;
 
import java.util.Random;
 
public class AluminiumTrade implements VillagerRegistry.IVillageTradeHandler {
 
    //村人の交換を追加する
    @Override
    public void manipulateTradesForVillager(EntityVillager villager, MerchantRecipeList recipeList, Random random) {
        //アルミニウム一個をエメラルド一個と交換する
        recipeList.add(new MerchantRecipe(new ItemStack(AluminiumMod.aluminium, 1), new ItemStack(Items.emerald, 1)));
 
        //アルミニウム鉱石一個とエメラルド二個とアルミニウム一個を交換する
        recipeList.add(new MerchantRecipe(new ItemStack(AluminiumMod.oreAluminium, 1),new ItemStack(Items.emerald, 1) ,
                new ItemStack(AluminiumMod.aluminium, 1)));
    }
}
 

  • AluminiumVillageCreation.java
package tutorial.aluminiummod;
 
import cpw.mods.fml.common.registry.VillagerRegistry;
import net.minecraft.util.MathHelper;
import net.minecraft.world.gen.structure.StructureVillagePieces;
 
import java.util.List;
import java.util.Random;
 
public class AluminiumVillageCreation implements VillagerRegistry.IVillageCreationHandler {
 
    //その村人がどのくらいの頻度で生成されるかを決める
    @Override
    public StructureVillagePieces.PieceWeight getVillagePieceWeight(Random random, int i) {
        return new StructureVillagePieces.PieceWeight(getComponentClass(), 50, MathHelper.getRandomIntegerInRange(random, i, i + 1));
    }
 
    //建築物のクラスを渡す
    @Override
    public Class<?> getComponentClass() {
        return AluminiumHouse.class;
    }
 
    //建築物生成時に呼ばれる
    @Override
    public Object buildComponent(StructureVillagePieces.PieceWeight villagePiece, StructureVillagePieces.Start startPiece, List pieces, 
        Random random, int p1, int p2, int p3, int p4, int p5) {
        return AluminiumHouse.func_74915_a(startPiece, pieces, random, p1, p2, p3, p4, p5);
    }
}
 

  • AluminiumHouse.java
package tutorial.aluminiummod;
 
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraft.world.gen.structure.StructureBoundingBox;
import net.minecraft.world.gen.structure.StructureComponent;
import net.minecraft.world.gen.structure.StructureVillagePieces;
import net.minecraftforge.common.ChestGenHooks;
 
import java.util.List;
import java.util.Random;
 
import static net.minecraftforge.common.ChestGenHooks.VILLAGE_BLACKSMITH;
 
public class AluminiumHouse extends StructureVillagePieces.Village {
 
    private boolean hasMadeChest;
 
    public AluminiumHouse() {
    }
 
    public AluminiumHouse(StructureVillagePieces.Start p_i2103_1_, int p_i2103_2_, Random p_i2103_3_, StructureBoundingBox p_i2103_4_, int p_i2103_5_) {
        super(p_i2103_1_, p_i2103_2_);
        this.coordBaseMode = p_i2103_5_;
        this.boundingBox = p_i2103_4_;
    }
 
    //構造物を生成するメソッド
    public boolean addComponentParts(World p_74875_1_, Random p_74875_2_, StructureBoundingBox p_74875_3_) {
        if (this.field_143015_k < 0) {
            this.field_143015_k = this.getAverageGroundLevel(p_74875_1_, p_74875_3_);
 
            if (this.field_143015_k < 0) {
                return true;
            }
 
            this.boundingBox.offset(0, this.field_143015_k - this.boundingBox.maxY + 6 - 1, 0);
        }
 
        //ブロックを指定した範囲内に敷き詰める
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 1, 0, 9, 4, 6, Blocks.air, Blocks.air, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 0, 0, 9, 0, 6, Blocks.cobblestone, Blocks.cobblestone, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 4, 0, 9, 4, 6, Blocks.cobblestone, Blocks.cobblestone, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 5, 0, 9, 5, 6, Blocks.stone_slab, Blocks.stone_slab, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 1, 5, 1, 8, 5, 5, Blocks.air, Blocks.air, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 1, 1, 0, 2, 3, 0, Blocks.iron_block, Blocks.iron_block, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 1, 0, 0, 4, 0, Blocks.log, Blocks.log, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 3, 1, 0, 3, 4, 0, Blocks.log, Blocks.log, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 1, 6, 0, 4, 6, Blocks.log, Blocks.log, false);
        //指定した相対座標にブロックを設置する
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.iron_block, 0, 3, 3, 1, p_74875_3_);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 3, 1, 2, 3, 3, 2, Blocks.iron_block, Blocks.iron_block, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 4, 1, 3, 5, 3, 3, Blocks.iron_block, Blocks.iron_block, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 0, 1, 1, 0, 3, 5, Blocks.iron_block, Blocks.iron_block, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 1, 1, 6, 5, 3, 6, Blocks.iron_block, Blocks.iron_block, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 5, 1, 0, 5, 3, 0, Blocks.fence, Blocks.fence, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 9, 1, 0, 9, 3, 0, Blocks.fence, Blocks.fence, false);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 6, 1, 4, 9, 4, 6, Blocks.cobblestone, Blocks.cobblestone, false);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.flowing_lava, 0, 7, 1, 5, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.flowing_lava, 0, 8, 1, 5, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.iron_bars, 0, 9, 2, 5, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.iron_bars, 0, 9, 2, 4, p_74875_3_);
        this.fillWithBlocks(p_74875_1_, p_74875_3_, 7, 2, 4, 8, 2, 5, Blocks.air, Blocks.air, false);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.cobblestone, 0, 6, 1, 3, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.furnace, 0, 6, 2, 3, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.furnace, 0, 6, 3, 3, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.double_stone_slab, 0, 8, 1, 1, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.glass_pane, 0, 0, 2, 2, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.glass_pane, 0, 0, 2, 4, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.glass_pane, 0, 2, 2, 6, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.glass_pane, 0, 4, 2, 6, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.fence, 0, 2, 1, 4, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.wooden_pressure_plate, 0, 2, 2, 4, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.iron_block, 0, 1, 1, 5, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.stone_stairs, this.getMetadataWithOffset(Blocks.stone_stairs, 3), 2, 1, 5, p_74875_3_);
        this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.stone_stairs, this.getMetadataWithOffset(Blocks.stone_stairs, 1), 1, 1, 4, p_74875_3_);
        int i;
        int j;
 
        if (!this.hasMadeChest) {
            i = this.getYWithOffset(1);
            j = this.getXWithOffset(5, 5);
            int k = this.getZWithOffset(5, 5);
 
            if (p_74875_3_.isVecInside(j, i, k)) {
                this.hasMadeChest = true;
                this.generateStructureChestContents(p_74875_1_, p_74875_3_, p_74875_2_, 5, 1, 5, ChestGenHooks.getItems(VILLAGE_BLACKSMITH, p_74875_2_), ChestGenHooks.getCount(VILLAGE_BLACKSMITH, p_74875_2_));
            }
        }
 
        for (i = 6; i <= 8; ++i) {
            if (this.getBlockAtCurrentPosition(p_74875_1_, i, 0, -1, p_74875_3_).getMaterial() == Material.air && this.getBlockAtCurrentPosition(p_74875_1_, i, -1, -1, p_74875_3_).getMaterial() != Material.air) {
                this.placeBlockAtCurrentPosition(p_74875_1_, Blocks.stone_stairs, this.getMetadataWithOffset(Blocks.stone_stairs, 3), i, 0, -1, p_74875_3_);
            }
        }
 
        for (i = 0; i < 7; ++i) {
            for (j = 0; j < 10; ++j) {
                this.clearCurrentPositionBlocksUpwards(p_74875_1_, j, 6, i, p_74875_3_);
                this.func_151554_b(p_74875_1_, Blocks.cobblestone, 0, j, -1, i, p_74875_3_);
            }
        }
 
        this.spawnVillagers(p_74875_1_, p_74875_3_, 7, 1, 1, 1);
        return true;
    }
 
    //家に生成させる村人のIDを渡す
    protected int getVillagerType(int p_74888_1_) {
        return AluminiumMod.metalVillagerID;
    }
 
    //以下 StructureVillagePieces.Village.House2よりコピペ
    public static AluminiumHouse func_74915_a(StructureVillagePieces.Start p_74915_0_, List p_74915_1_, Random p_74915_2_, int p_74915_3_, int p_74915_4_, int p_74915_5_, int p_74915_6_, int p_74915_7_) {
        StructureBoundingBox structureboundingbox = StructureBoundingBox.getComponentToAddBoundingBox(p_74915_3_, p_74915_4_, p_74915_5_, 0, 0, 0, 10, 6, 7, p_74915_6_);
        return canVillageGoDeeper(structureboundingbox) && StructureComponent.findIntersecting(p_74915_1_, structureboundingbox) == null ? new AluminiumHouse(p_74915_0_, p_74915_7_, p_74915_2_, structureboundingbox, p_74915_6_) : null;
    }
 
    protected void func_143012_a(NBTTagCompound p_143012_1_) {
        super.func_143012_a(p_143012_1_);
        p_143012_1_.setBoolean("Chest", this.hasMadeChest);
    }
 
    protected void func_143011_b(NBTTagCompound p_143011_1_) {
        super.func_143011_b(p_143011_1_);
        this.hasMadeChest = p_143011_1_.getBoolean("Chest");
    }
}
 
 

解説

VillagerRegistry

void registerVillagerId(int id)

村人のIDを登録するメソッド。
コメントにある通り、村人IDは0~4が既存の村人として設定されているため、それ以外の数値を与える必要がある。
競合を防ぐためには、config等で変更できるようにするとよい。
また、IDはいろいろなところで使うためstatic finalな定数に格納すると便利。

void registerVillageTradeHandler(int villagerId, IVillageTradeHandler handler)

指定したIDの村人に特定の交換を登録するメソッド。
なお、すべての村人(MOD追加されたもの含む)に特定の交換を実装するなら、以下のようなコードが効率的。
(VillagerRegistry.instance().registerVillageTradeHandler(metalVillagerID, new AluminiumTrade());の部分を以下のコードに書き換える。)
for(Integer integer : VillagerRegistry.getRegisteredVillagers()){
            VillagerRegistry.instance().registerVillageTradeHandler(integer, new AluminiumTrade());
        }
 

void registerVillagerSkin(int villagerId, ResourceLocation villagerSkin)

指定したIDの村人にスキンを追加するメソッド。

void registerVillageCreationHandler(IVillageCreationHandler handler)

村人の家と村人の生成を登録するメソッド。

MapGenStructureIO

ワールドへの構造物生成を管理するクラス。

void registerStructure(Class p_143034_0_, String p_143034_1_)

void func_143031_a(Class p_143031_0_, String p_143031_1_)

どちらも構造物を登録するメソッド。
registerStructureを先に呼ぶ。

MerchantRecipe

これを使ったインスタンスをIVillageTradeHandlerを実装したクラスのmanipulateTradesForVillagerで、第二引数recipeListに追加して村人の交換を管理する。

コンストラクター(ItemStack p_i1942_1_, ItemStack p_i1942_2_)

村人の交換を実装するメソッド。第一引数に渡すもの、第二引数に受け取るものを渡す。
下のコンストラクターを用いてnew MerchantRecipe(p_i1942_1_, (ItemStack)null, p_i1942_2_);と記述するのと同じ意味。

コンストラクター(ItemStack p_i1941_1_, ItemStack p_i1941_2_, ItemStack p_i1941_3_)

村人の交換を実装するメソッド。第一引数・第二引数に渡すもの、第三引数に受け取るものを渡す。

StructureComponent

StructureVillagePiecesはこのクラスを継承している。

void fillWithBlocks(World worldObj, StructureBoundingBox structBB, int minX, int minY, int minZ, int maxX, int maxY, int maxZ

, Block placeBlock, Block replaceBlock, boolean alwaysreplace)
x,y,z(structBBで指定された直方体内の相対座標)座標にブロックを敷き詰める。
min**は始点、max**は終点を示している。また、placeBlockは先に設置するブロック、replaceBlockは後に設置するブロック、(基本同じ物を渡す)、alwaysreplaceはreplaceBlockを使用するか否かを
それぞれ示している。

コメント

この項目に関する質問などをどうぞ。
名前: