※2018/08/18 ちょっと改良。
どもです。
僕はマイクラ1.7.10民。国内産の高性能な建材や食べ物を追加してくれるMODが豊富ですね。
しかし、食べ物が豊かになると、ちょっとした不満が生じるものです。
なぜ、メイドさんは砂糖しか食べてくれないのだろう、という。
折角美味しそうなご飯が沢山あるのに!レアな食材を集めて手間暇かけて料理を作っても、
可愛いメイドさんに食べさせてあげられないなんて悲しいと思いませんか???
ですので、メイドさんがご飯を食べられるようにします。
用意しますのは、みんな大好きLittleMaidMobX。
最初はアドオンを作る心算だったのですが、ちょっと僕の技術では厳しかったので、改造します。
まず、メイドさんが砂糖を食べるロジックがどこにあるのかを探します。
そして見つけたのがLMM_EntityLittleMaid.classの1828行目。
if (!worldObj.isRemote) {
if (getSwingStatusDominant().canAttack()) {
// mod_LMM_littleMaidMob.Debug("isRemort:" + worldObj.isRemote);
// 回復
if (getHealth() < getMaxHealth()) {
if (maidInventory.consumeInventoryItem(Items.sugar)) {
eatSugar(true, false);
}
}
// つまみ食い
if (rand.nextInt(50000) == 0 && maidInventory.consumeInventoryItem(Items.sugar)) {
eatSugar(true, false);
}
// 契約更新
if (isContractEX()) {
float f = getContractLimitDays();
if (f <= 6 && maidInventory.consumeInventoryItem(Items.sugar)) {
// 契約更新
eatSugar(true, true);
}
}
}
}
consumeInventoryItem(Items.sugar)→インベントリから砂糖を1つ消費できたらtrueを返す
eatSugar()→砂糖を食べた後の処理
つまるところ、このconsumeInventoryItem()の引数に「インベントリ内の食べ物」を当てられれば良いよね!
ということで「インベントリ内の食べ物」を返す関数を作って近くに配置しておきます。
public Item eatFoodSelect() {
//*mochi. 砂糖の代わりにごはんを食べる
Item currentfood = null;
int i;
ArrayList<Integer> ar = new ArrayList<Integer>();
boolean sw = false;
for(i=0; i<18; i++){
ItemStack litemstack = maidInventory.getStackInSlot(i);
if (litemstack != null) {
Item cu = litemstack.getItem();
if (cu instanceof ItemFood) {
//食べ物判定に合格
ar.add(i);
sw = true;
}
}
}
if(sw){
while(true){
int ran = rand.nextInt(18);
if(ar.contains(ran)) {
currentfood = maidInventory.getStackInSlot(ran).getItem();
break;
}
}
}
return currentfood;
}
やってることは極めて単純。
インベントリの頭から末尾まで参照して「食べ物」ならそのインベントリ番号を取得。
その中からランダムで選んだアイテムをItem属性でreturnするだけ。
また、食べ物かどうかの判定は、ヒーラーモードの切り替えと同等の「ItemFoodクラスのサブクラスである」ことを条件としました。
これでOK。あとは先ほどの処理をちょいと書き換えて…
// つまみ食い
if (rand.nextInt(50000) == 0) {
//*mochi. 砂糖の代わりにごはんを食べる
Item currentfood = eatFoodSelect();
if(currentfood != null && maidInventory.consumeInventoryItem(currentfood)){
eatSugar(true, false);
}
}
// 契約更新
if (isContractEX()) {
float f = getContractLimitDays();
if (f <= 6) {
//*mochi. 砂糖の代わりにごはんを食べる
Item currentfood = eatFoodSelect();
if(currentfood != null && maidInventory.consumeInventoryItem(currentfood)){
// 契約更新
eatSugar(true, true);
}
}
}
これで良し、と。
ここの処理は毎フレーム?され続けてるから、負担にならないようeatSugarの直前で実行するよう心がけよう。
さあ、完成だ!
と言いたいところですが、実は大きな落とし穴がありまして。
AMT2や豆腐Craftの一部食べ物が「ItemFoodクラスのサブクラス」ではないんですねこれが!!!
MODで増えた美味しい食べ物をメイドさんに食べてもらいたいのに、それらが料理と判定されないんじゃ意味がないじゃない!!!!
アイテムのクラスのメソッド名を拾ってみようか。
Class cl = cu.getClass();
for(Method m : cl.getDeclaredMethods()) {
if(m.getName()=="onEaten"){
ar.add(i);
sw = true;
}
}
これでイケる、と思ったんだが…豆腐Craftのアイテム構成がかなり特殊で、アイテム単体のクラスに食べる処理が入ってないという悲劇が判明。
さーて、こりゃもう最後の手段しかないね。
MODの食べ物クラスを名指ししてみようか。
Class cl = cu.getClass();
String na = cl.getName();
if("tsuteto.tofu.item.ItemFoodSet".equals(na)){
//特定のクラス名は合格!
ar.add(i);
sw = true;
}
}
豆腐は殆どの食べ物アイテムが1クラスで設定されてるので、そのクラス名を直に指定!
まとめてみよう
public Item eatFoodSelect() {
//*mochi. 砂糖の代わりにごはんを食べる
Item currentfood = null;
int i;
ArrayList<Integer> ar = new ArrayList<Integer>();
boolean sw = false;
for(i=0; i<18; i++){
boolean sw2 = false;
ItemStack litemstack = maidInventory.getStackInSlot(i);
if (litemstack != null) {
Item cu = litemstack.getItem();
if (cu instanceof ItemFood) {
//バニラや一部MODの食べ物ならこれだけで比較OK
sw2 = true;
}else{
//AMTとか豆腐とかに対応すると…めっさ処理増えるの…。
Class cl = cu.getClass();
String na = cl.getName();
if("net.minecraft.item.Item".equals(na)){
//メソッド比較で引っかかるので、根本的なItemクラスはまず除外。
}else if("tsuteto.tofu.item.ItemFoodSet".equals(na)){
//特定のクラス名は合格!
sw2 = true;
}else{
for(Method m : cl.getDeclaredMethods()) {
String mna = m.getName();
if("onEaten".equals(mna) || "getPotionEffect".equals(mna) || "hungerOnEaten".equals(mna)){
//食べる系のメソッドがあったら合格!
sw2 = true;
break;
}
}
}
}
if(sw2){
//食べ物判定に合格
ar.add(i);
sw = true;
}
}
}
if(sw){
i = 0;
while(true){
//「食べ物」のあるインベントリから、ランダムで1種を選出。
int ran = rand.nextInt(18);
if(ar.contains(ran)) {
currentfood = maidInventory.getStackInSlot(ran).getItem();
break;
}
++i;
if(i>100){
//100回試行して失敗したら先頭の食べ物を取得
if(ar.get(0) != null) {
currentfood = maidInventory.getStackInSlot(ar.get(0)).getItem();
}
break;
}
}
}
return currentfood;
}
自分のデータで動かしてみているけど、今のところ想定外の動作やエラーで落ちるなんかは無し。
メイドのコアプログラム自体が常時走っていることを考えると、たぶんPCへの負担もほぼ誤差の範疇だと思う。