Modding > TurtleUpgrade

「Modding/TurtleUpgrade」の編集履歴(バックアップ)一覧に戻る
Modding/TurtleUpgrade」を以下のとおり復元します。
ComputerCraftにTurtleアップグレードを追加するModの製作について解説する。
&color(red){対象:Minecraft Forgeを利用して独自のレシピを追加できるようなModding初級者以上。}

参考資料:
-[[Minecraft Midding Wiki>http://minecraftjp.info/modding/index.php/Minecraft_Modding_Wiki]]
--[[チュートリアル一覧: MinecraftForgeUniversal>http://minecraftjp.info/modding/index.php/%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E4%B8%80%E8%A6%A7#MinecraftForgeUniversal]]
-[[ComputerCraft Forums: Peripherals and Turtle Upgrades>http://www.computercraft.info/forums2/index.php?/forum/17-]]
--[[Creating Peripherals and Turtle Upgrades>http://www.computercraft.info/forums2/index.php?/topic/606-]]
-[[ComputerCraft Wiki: Turtle Upgrade IDs>http://www.computercraft.info/wiki/index.php?title=Turtle_Upgrade_IDs]]
-[[Minecraft Forge: Tutorials>http://www.minecraftforge.net/wiki/Category:Tutorials]]
執筆時のバージョン:
-ComputerCraft 1.52 for Minecraft 1.5.1

----
#contents
----

*Turtleアップグレード追加Modの概要
Turtleアップグレードとは、Digging TurtleやCrafty Turtleのように、Turtleにアイテムやブロックを装着(クラフト)することによって機能を追加することができるシステムである。

**アップグレードの実装
TurtleアップグレードはITurtleUpgradeインターフェイスの実装クラスとして機能を実装し、その実装クラスをComputerCraftに登録することによってTurtleに装着できるようになる。機能追加に関するAPIも用意されているため、簡単に作ることができる。

**アップグレードのタイプ
Turtleアップグレードには以下の2タイプがある。
-[[turtle.digやturtle.attack>API/Turtle]]で作動する&bold(){Tool}タイプ(例:ダイヤシャベルでDigging Turtle)
-[[Peripheral API>API/Peripheral]]で作動する&bold(){Peripheral}タイプ(例:作業台でCrafty Turtle)
Turtleアップグレードを製作するときは、どちらか一方のタイプを選択する必要がある。今のところ1つのTurtleには、1つのToolタイプと1つのPeripheralタイプ、または2つのPeripheralタイプを装着することができる(例:ダイヤクワ(Tool)とWireless Modem(Peripheral)でWireless Farming Turtle)。

**アップグレードID
Turtleアップグレードには他のTurtleアップグレードと重複しないID番号を割り当てる必要がある。Mod製作者が割り当て可能なIDの範囲は64~255である。既に配布されているModが使用しているTurtleアップグレードIDは[[ComputerCraft Wiki>http://www.computercraft.info/wiki/index.php?title=Turtle_Upgrade_IDs]]で確認することができる。

*基本的なTurtleアップグレード追加Modの例
ToolタイプとPeripheralタイプのTurtleアップグレードを追加する。

このサンプルは以下の4クラスからなる。
-BasicUpgrades
-TurtleBasicTool
-TurtleBasicPeripheral
-PeripheralTurtleBasicPeripheral
簡略化のため[[Proxyシステム>http://minecraftjp.info/modding/index.php/%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6]]は利用していない。

なお、このサンプルmodの前提modは【MinecraftForge】と【ComputerCraft】である。

**BasicUpgrades.java
@ModアノテーションによりForgeModLoaderにロードされる。Modのメインクラス。 

#highlight(java){{
package sample.upgrade;

import net.minecraftforge.common.Configuration;
import net.minecraftforge.common.Property;
import cpw.mods.fml.common.Loader;
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.network.NetworkMod;
import dan200.turtle.api.TurtleAPI;

@Mod(modid="BasicUpgrades", name="BasicUpgrades", version="0.0.0", dependencies="after:CCTurtle")
@NetworkMod(clientSideRequired=true, serverSideRequired=false)
public class BasicUpgrades {

	public static int basicToolUpgradeID;
	public static int basicPeripheralUpgradeID;

	@Mod.PreInit
	public void preInit(FMLPreInitializationEvent event)
	{
		Property Prop;
		Configuration cfg = new Configuration(event.getSuggestedConfigurationFile());
		cfg.load();

		Prop = cfg.get("upgrade", "basicToolUpgradeID", 110);
		basicToolUpgradeID = Prop.getInt();
		Prop = cfg.get("upgrade", "basicPeripheralUpgradeID", 111);
		basicPeripheralUpgradeID = Prop.getInt();

		cfg.save();
	}

	@Mod.Init
	public void init(FMLInitializationEvent event)
	{
		TurtleAPI.registerUpgrade(new TurtleBasicTool());
		TurtleAPI.registerUpgrade(new TurtleBasicPeripheral());
	}
} }}

@Modアノテーションを付加したクラスがForge Mod Loaderにロードされる。ここでModの情報も登録している。特筆すべきは&bold(){dependencies="after:CCTurtle"}という値で、これはこのModをComputerCraftよりも後に読み込ませる効果がある。

@Mod.PreInitアノテーションを付加したメソッドpreInit(@Mod.Initメソッドの前に呼び出される)でコンフィグファイルを読み込み、TurtleアップグレードIDのブロックIDを取得している。ToolアップグレードIDのデフォルト値は&bold(){110}、PeripheralアップグレードIDのデフォルト値は&bold(){111}。

@Mod.Initアノテーションを付加したメソッドinit(初期化時に呼び出される)でComputerCraftにTurtleアップグレードを登録している。TurtleアップブレードのクラスをTurtleAPI.registerUpgrade()で登録することにより、そのTurtleアップグレードがゲーム中で使用可能になる。

**TurtleBasicTool.java
ITurtleUpgradeの実装クラスでToolタイプのTurtleアップグレードを実装している。このクラスで、アップグレードをTurtleに装着するためのアイテム(装着レシピはComputerCraftによる自動追加)や、装着後の外観、採掘・攻撃時の動作などを指定することができる。

このサンプルでは、簡単な採掘と攻撃ができるようになっている。

#highlight(java){{
package sample.upgrade;

import java.util.ArrayList;
import java.util.Iterator;

import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Facing;
import net.minecraft.util.Icon;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import dan200.computer.api.IHostedPeripheral;
import dan200.turtle.api.ITurtleAccess;
import dan200.turtle.api.ITurtleUpgrade;
import dan200.turtle.api.TurtleSide;
import dan200.turtle.api.TurtleUpgradeType;
import dan200.turtle.api.TurtleVerb;

public class TurtleBasicTool implements ITurtleUpgrade
{
	public ItemStack upgradeItem = new ItemStack(Item.pickaxeGold);	
	
	@Override
	public int getUpgradeID()
	{
		return BasicUpgrades.basicToolUpgradeID;
	}

	@Override
	public String getAdjective()
	{
		return "Tool";
	}

	@Override
	public TurtleUpgradeType getType()
	{
		return  TurtleUpgradeType.Tool;
	}

	@Override
	public ItemStack getCraftingItem()
	{
		return upgradeItem;
	}

	@Override
	public boolean isSecret()
	{
		return false;
	}

	@Override
	public IHostedPeripheral createPeripheral(ITurtleAccess turtle,
			TurtleSide side)
	{
		return null;
	}

	@Override
	public boolean useTool(ITurtleAccess turtle, TurtleSide side,
			TurtleVerb verb, int direction)
	{
		switch( verb )
		{
		case Dig:
			return dig(turtle, direction);
		case Attack:
			return attack(turtle, direction);
		}
		return false;
	}

	private boolean dig(ITurtleAccess turtle, int dir)
	{
		World world = turtle.getWorld();
		Vec3 position = turtle.getPosition();
		
		if (position == null)
		{
			return false;
		}
		
		int newX = (int)position.xCoord + Facing.offsetsXForSide[dir];
		int newY = (int)position.yCoord + Facing.offsetsYForSide[dir];
		int newZ = (int)position.zCoord + Facing.offsetsZForSide[dir];
	
		if ( (newY < 0) || (newY >= world.getHeight()) )
		{
			return false;
		}

		int blockID = world.getBlockId(newX, newY, newZ);
		Block block = Block.blocksList[blockID];		
		
		if ( (blockID == 0)
			|| (blockID == Block.bedrock.blockID)
			|| (block.getBlockHardness(world, newX, newY, newZ) <= -1.0F)
			)
		{
			return false;
		}

		int matadata = world.getBlockMetadata(newX, newY, newZ);
		ArrayList items = block.getBlockDropped(world, newX, newY, newZ, matadata, 0);
		Iterator it = items.iterator();
		
		while (it.hasNext())
		{
			ItemStack item = (ItemStack)it.next();
			if ( !turtle.storeItemStack(item) )
			{
				int[] oppositeSide = { 1, 0, 3, 2, 5, 4 };
				if ( !turtle.dropItemStack(item, oppositeSide[turtle.getFacingDir()]) )
				{
					turtle.dropItemStack(item, turtle.getFacingDir());
				}
			}
		}

		world.playAuxSFX(2001, newX, newY, newZ, blockID + matadata * 4096);
		world.setBlock(newX, newY, newZ, 0, 0, 3);

		return true;
	}

	private boolean attack(ITurtleAccess turtle, int dir)
	{
		return turtle.attackWithItemStack(new ItemStack(Item.swordDiamond), dir, 2.0F);
	}

	@Override
	public Icon getIcon(ITurtleAccess turtle, TurtleSide side) {

		return upgradeItem.getIconIndex();
	}

} }}

TurtleアップグレードIDはgetUpgradeIDの戻り値で指定する。このサンプルでは前述の@MODクラスBasicUpgradesのメンバー変数から取得して指定している。アップグレードのタイプはenum TurtleUpgradeTypeの列挙子でToolを指定。装着したTurtleに付く形容詞は"Tool"を指定(つまり"Tool Turtle"になる)。クラフトに必要なアイテムと装着したアップグレードの外観は金のツルハシ。Toolタイプなので無用なcreatePeripheralではnullを返す。

Toolタイプの本領はuseToolによって発揮される。このサンプルではverbの種類(DigかAttackか)で分岐させた後、それぞれ別のメンバー関数(digとattack)に処理させている。
-dig
Turtleに採掘させる場合、採掘に必要な処理そのものを書く必要がある。
まず、useToolから得たITurtleAccess turtleによりTurtleの座標を得て、その座標と採掘する方向dirから採掘すべきブロックの座標と種類、メタデータを取得する。次に、採掘すべきブロックが採掘できるか(高度、種類)を調べる。採掘可能だった場合は採掘して得られるアイテムを取得し、スロットに入りきらなかった場合はドロップする。最後に採掘の効果音を鳴らし、採掘したブロックを消す(空気ブロックにする)。
-attack
採掘処理が難しい一方で、攻撃は専用メソッドが用意されているため簡単である。このサンプルではダイヤソードの2倍の攻撃力14(ハート7個分)でdir方向を攻撃する。

ちなみに、それぞれのverdは単にTurtle側のturtle.dig()とturtle.attack()で呼び出されるというだけで、必ずしもTurtleが採掘や攻撃を行わなければならないという訳ではない。

**TurtleBasicPeripheral.java
ITurtleUpgradeの実装クラスでPeripheralタイプのTurtleアップグレードを実装している。

#highlight(java){{
package sample.upgrade;

import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Icon;
import dan200.computer.api.IHostedPeripheral;
import dan200.turtle.api.ITurtleAccess;
import dan200.turtle.api.ITurtleUpgrade;
import dan200.turtle.api.TurtleSide;
import dan200.turtle.api.TurtleUpgradeType;
import dan200.turtle.api.TurtleVerb;

public class TurtleBasicPeripheral implements ITurtleUpgrade
{
	public ItemStack upgradeItem = new ItemStack(Block.stone, 1, 0);

	@Override
	public int getUpgradeID() {
		return BasicUpgrades.basicPeripheralUpgradeID;
	}

	@Override
	public String getAdjective() {
		return "Peripheral";
	}

	@Override
	public TurtleUpgradeType getType() {
		return TurtleUpgradeType.Peripheral;
	}

	@Override
	public ItemStack getCraftingItem() {
		return upgradeItem;
	}

	@Override
	public boolean isSecret() {
		return false;
	}

	@Override
	public IHostedPeripheral createPeripheral(ITurtleAccess turtle,
			TurtleSide side) {
		return new PeripheralTurtleBasicPeripheral(turtle, side);
	}

	@Override
	public boolean useTool(ITurtleAccess turtle, TurtleSide side,
			TurtleVerb verb, int direction) {
		return false;
	}

	@Override
	public Icon getIcon(ITurtleAccess turtle, TurtleSide side) {
		return Block.stoneSingleSlab.getBlockTextureFromSide(0);
	}

} }}

TurtleアップグレードIDはgetUpgradeIDの戻り値で指定する。このサンプルでは前述の@MODクラスBasicUpgradesのメンバー変数から取得して指定している。アップグレードのタイプはenum TurtleUpgradeTypeの列挙子でPeripheralを指定。装着したTurtleに付く形容詞は"Peripheral"を指定(つまり"Peripheral Turtle"になる)。クラフトに必要なアイテムは石ブロック。装着したアップグレードの外観は石ハーフブロックの下面。

Peripheralタイプなので、createPeripheralでは周辺機器の機能を実装したIHostedPeripheralの実装クラスのインスタンスを返す。このサンプルでは、後述のPeripheralTurtleBasicPeripheralのインスタンスを返している。ToolタイプではないのでuseToolは呼び出されない。

**PeripheralTurtleBasicPeripheral.java
IHostedPeripheralの実装クラスでPeripheralタイプのアップグレードの周辺機器としての動作を実装している。IHostedPeripheralはIPeripheralのサブインターフェイスである。[[周辺機器ブロック追加Mod>Modding/Peripheral]]も参照のこと。

#highlight(java){{
package sample.upgrade;

import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.Vec3;
import dan200.computer.api.IComputerAccess;
import dan200.computer.api.IHostedPeripheral;
import dan200.turtle.api.ITurtleAccess;
import dan200.turtle.api.TurtleSide;

public class PeripheralTurtleBasicPeripheral implements IHostedPeripheral
{
	private ITurtleAccess m_turtle;
	private IComputerAccess m_computer;
	private TurtleSide m_side;

	PeripheralTurtleBasicPeripheral(ITurtleAccess turtle, TurtleSide side)
	{
		m_turtle = turtle;
		m_side = side;
	}

	@Override
	public String getType()
	{
		return "turtlebasic";
	}

	@Override
	public String[] getMethodNames()
	{
		return new String[] { "test", "getPosition" };
	}

	@Override
	public Object[] callMethod( IComputerAccess computer, int method, Object[] arguments )
			throws Exception
	{
		switch( method )
		{
		case 0:
			// test
			if( arguments.length < 1 ) {
				throw new Exception("Expected argument");
			}
			return new Object[] { arguments[0] };

		case 1:
			//getPosition
			Vec3 pos = m_turtle.getPosition();
			if( arguments.length > 0
					&& arguments[0] instanceof Boolean
					&& true == (Boolean)arguments[0])
			{
				return new Object[] {
						"Position: " + (int)pos.xCoord
						+ ", " + (int)pos.yCoord
						+ ", " + (int)pos.zCoord
						};
			}
			return new Object[] {
					Integer.valueOf((int)pos.xCoord),
					Integer.valueOf((int)pos.yCoord),
					Integer.valueOf((int)pos.zCoord)
					};
		}

		return null;
	}

	@Override
	public boolean canAttachToSide(int side)
	{
		return true;
	}

	@Override
	public void attach( IComputerAccess computer)
	{
		m_computer = computer;

		System.out.printf("[TurtleBasicPeripheral] Attached to Computer #%d (side: %s)\n",
				m_computer.getID(), m_computer.getAttachmentName()
				);
	}

	@Override
	public void detach( IComputerAccess computer )
	{
		System.out.printf("[TurtleBasicPeripheral] Detached from Computer #%d\n",
				computer.getID()
				);
	}

	@Override
	public void update()
	{

	}

	@Override
	public void readFromNBT(NBTTagCompound nbttagcompound) {
		
	}

	@Override
	public void writeToNBT(NBTTagCompound nbttagcompound) {
		
	}

} }}

[[周辺機器ブロック追加Mod>Modding/Peripheral]]と似たような構成になっているが、以下のような点が違う。
-attachはTurtle起動時、detachはattach後のTurtle破壊時に呼び出される(Turtleアップグレードの特徴)
-attach後に毎Tick呼び出されるupdateが追加されている(IHostedPeripheralの特徴)
-NBTへアクセスするメソッドが追加されている(IHostedPeripheralの特徴)

このサンプルではタートルへのアクセスインターフェイスを取得するために、引数付きのコンストラクタを追加している。これは周辺機器側からタートルへアクセスするのに必要である。また、attach時にTurtle内のComputerのアクセスインターフェイスを取得している。周辺機器ブロックと違い、アップグレード周辺機器に接続するのは装着されたTurtleだけなので、それらは単なるメンバ変数に保存している。

周辺機器のメソッドとして、[[周辺機器ブロック追加Mod>Modding/Peripheral]]のサンプルと同様の"test"と、Turtleの座標を取得する"getPosition"を実装している。getPositionは引数にtrueを指定して呼ばれた時は座標を文字列にして返し、それ以外のときは座標を3つの数値で返す(サンプルではあるが、GPSから考えればチート性能なメソッドである)。

復元してよろしいですか?