提供: defeatedcrow mod wiki
移動先: 案内検索

<目次に戻る>

発射機能を作る

無機能だったItemに機能を追加していきます。

  1. 右クリック使用時に弾Entityを発射する
  2. アイテムの耐久値を消費せず、代わりにマガジンの弾を消費して弾を撃つようにする。耐久値ではないので、無くなっても破壊されない。
  3. 弾切れの時に右クリックすると、インベントリの弾薬を消費して補充する。
  4. 耐久値ゲージをマガジン用のゲージに使用する。
package defeatedcrow.flamethrower.item;

import java.util.List;

import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.ItemBow;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import defeatedcrow.flamethrower.entity.EntityFlame;

public class ItemIgnis extends ItemBow {

	public ItemIgnis() {
		this.setMaxStackSize(1);
		this.setMaxDamage(4);
		this.setNoRepair();
	}

	@Override
	@SideOnly(Side.CLIENT)
	public void registerIcons(IIconRegister par1IconRegister) {
		this.itemIcon = par1IconRegister.registerIcon("dcsflame:flamethrower");
	}

	@Override
	@SideOnly(Side.CLIENT)
	public boolean isFull3D() {
		return true;
	}

	/* ここから先が追加した部分 */

	// 耐久値を減らそうとする処理が呼ばれた場合に握りつぶす。アイテムロストなどの事故防止。
	@Override
	public void setDamage(ItemStack stack, int damage) {
		return;
	}

	// このメソッドは、プレイヤーがブロックをターゲットしていないときに呼ばれる方のRightClickメソッド。
	@Override
	public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer) {

		// クリエイティブモードか
		boolean creative = par3EntityPlayer.capabilities.isCreativeMode;

		// マガジンが空でないかどうか
		boolean hasCharge = false;
		if (par1ItemStack.getItem() == this) {
			// 弾薬減少処理。第三引数のbooleanをfalseにすると、実際には減らさないシミュレーション処理になる
			int c2 = this.discharge(par1ItemStack, 1, false);
			if (c2 > 0) {
				hasCharge = true;
				this.discharge(par1ItemStack, 1, true);
			}
		}

		// 消費できる弾があったら実行
		if (creative || hasCharge) {
			// ダメージ量
			float dam = 2.0F;
			// 0.0F~1.0Fの乱数
			float ram = par2World.rand.nextFloat();
			int cooltime = 0; // 一旦凍結。常に0を入れる
			// 弾速
			float speed = 1.0F;

			// ランダムに速度を変えながら、同時に4発ずつ発射する。
			for (int i = 0; i < 4; i++) {
				float f = speed * (i * 0.35F) + ram;
				// 生成
				EntityFlame bullet = new EntityFlame(par2World, par3EntityPlayer, f, 0.0F, dam, 4.0F, cooltime);
				// Entityの着火機能を利用
				bullet.setFire(100);
				// Entityのスポーン処理はisRemote判定を利用してサーバー側のみで行うこと!
				if (!par2World.isRemote) {
					par2World.spawnEntityInWorld(bullet);
				}
			}
		} else {
			// この処理に飛んでいるということは弾切れなので、弾薬を補充する処理をする
			// 今はまだ弾薬アイテムを追加前なので、仮のアイテムを消費させておく
			if (par3EntityPlayer.inventory.hasItem(Items.gunpowder)) {
				// 弾薬補充処理。マガジンはNBTタグに入っている
				NBTTagCompound nbt = par1ItemStack.getTagCompound();
				if (nbt == null) {
					nbt = new NBTTagCompound();
				}
				nbt.setInteger("dcf_magazine", 100);
				par1ItemStack.setTagCompound(nbt);
				// playerのインベントリの弾薬アイテムを減らす。この処理はInventoryPlayerの既存処理を利用している。
				par3EntityPlayer.inventory.consumeInventoryItem(Items.gunpowder);

				// バニラのドアガチャSEのピッチを弄って、それっぽくしている
				par2World.playSoundAtEntity(par3EntityPlayer, "random.door_close", 1.0F,
						1.0F / (itemRand.nextFloat() * 0.4F + 1.2F) + 1.3F);
			}
		}
		return par1ItemStack;
	}

	// onEatenのほうの右クリック処理ではそのままItemStackを返す(つまりなにもしない)
	@Override
	public ItemStack onEaten(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer) {
		return par1ItemStack;
	}

	// onItemRightClickで呼んでいる弾の消費処理
	// 今は使っていないが、弾薬を2以上減らす場合にも応用できるようにしている
	public int discharge(ItemStack item, int amount, boolean flag) {

		if (item == null)
			return 0;

		// NBTに入れている弾の数が足りているかの判定と、第三引数がtrueなら減少させている
		NBTTagCompound nbt = item.getTagCompound();
		int charge = 0;
		int reduce = 0;
		if (nbt != null && nbt.hasKey("magazine")) {
			charge = nbt.getInteger("magazine");
		}

		reduce = Math.min(amount, charge);

		if (flag) {
			if (nbt != null) {
				nbt.setInteger("magazine", (charge - reduce));
				item.setTagCompound(nbt);
			} else {
				NBTTagCompound nbt2 = new NBTTagCompound();
				nbt2.setInteger("magazine", (charge - reduce));
				item.setTagCompound(nbt2);
			}
		}

		return reduce;
	}

	// マウスオーバー時の表示情報。マガジンの残量/MAXを表示している。
	@Override
	@SideOnly(Side.CLIENT)
	public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4) {
		super.addInformation(par1ItemStack, par2EntityPlayer, par3List, par4);
		NBTTagCompound nbt = par1ItemStack.getTagCompound();
		int charge = 0;
		int max = 100;
		if (nbt != null && nbt.hasKey("magazine")) {
			charge = nbt.getInteger("magazine");
		}

		String s = new String("magazine : " + charge + "/" + max);
		par3List.add(s);
	}

	// 耐久値ゲージを表示するかどうかのフラグ
	@Override
	public boolean showDurabilityBar(ItemStack stack) {
		return true;
	}

	// このメソッドで耐久値の代わりに弾の残量をゲージ表示できるようにしている。
	// 0.0Dを返したときがMAXで、1.0Dを返すとゲージがなくなったように見える。よって、MAX - 残量 を計算して返している。
	@Override
	public double getDurabilityForDisplay(ItemStack stack) {
		NBTTagCompound nbt = stack.getTagCompound();
		int charge = 0;
		int max = 100;
		if (nbt != null && nbt.hasKey("magazine")) {
			charge = nbt.getInteger("magazine");
			charge = MathHelper.clamp_int(charge, 0, max);
		}

		int i = max - charge;
		return (double) i / (double) max;
	}

}

主にやっていることは2つ。

public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer)

で、発射機構。

public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4)
public double getDurabilityForDisplay(ItemStack stack)

この2つのメソッドでアイテムの弾薬情報表示。


ここまで作れば、ゲーム中で実際に発射して試すことが出来る。
ignis_entity.png
一度に4発ずつ出しているので、炎パーティクルのボリューム感は十分な気がするし、雰囲気はでていると思う。右クリック押しっぱなしでエイム不要な点もまぁまぁ再現されているかなと思う。
何ともいえない射程の長さや弾切れの早さ、無強化の状態でのパッとしない攻撃力、視界の悪さなど、IGNISっぽさが出てきたなーと思えば次の段階へ。
もちろん、作る人によっては強すぎる/弱すぎるなど感覚に違いがあると思う。その場合はdamageやspeedなどのパラメータやEntityのヒット処理での当たり判定チェック範囲のサイズなど、少しずつ変えて確かめながらバランスを調整すると良いと思う。自分が一番しっくりくる値に決めればOK。