2014年10月07日

(SimPeople)ヘルパーにベランダの椅子に座ってもらう

ヘルパー呼び出し絵画で呼んだヘルパーは外に置いてある椅子に座ろうとします。でも、ベランダなどは外と認識してくれません。ウチは食堂が外まで遠いので、出来るならベランダに置いた椅子に座ってほしい、というわけでその辺をいじってみます。



helper2-1.jpg
こちらはヘルパー呼び出し絵画の中の人、
NPC_Vacation_JanitorさんのBHAV #4114:"sit down"です。呼び出し絵画についてくるNPCを開けば、大抵この人になっているはずです。
ざっくり見ていくと、2行目の「Find Best Object for Function」で椅子を検索して、更にその椅子が"Room 0"に置いてあるか調べています。

2行目を見ると、HEXの7バイト目が1です。すなわち、前回解説したように外にある椅子を検索しています。ここでの「外」は、要は普通の壁に囲まれていない場所です。柵に囲まれている場所や、ベランダ的な場所もここに含まれます。
ベランダに壁と窓仕様の手すりを付けると、壁に囲まれているので屋内と認識されます。ラグベースの手すりを使ったり、1タイル穴を空けておいて適当なオブジェクトを置いておく事で屋外にすることが出来ます。

更にRoomが0かどうかをチェックしていますが、これは「一番外のRoomであるか」という意味です。なんだか意味が分かりませんね。

helper2-7.jpg
ちょっとわかりづらいんですが、シムピでは外も部屋の1つとして認識します。Lot全体が「Room0の屋外」という部屋扱いです。部屋なのに屋外とは変ですが、"壁で区切られた場所をそれぞれRoomと呼ぶ、Lot全体も1つのRoomである"という感じです。
つまり、Lotの外まで繋がっている(壁で区切られていない)場所がRoom0です。2階は別の部屋扱いなので、ベランダは「屋外だけれども、Room0ではない場所」となっています。壁と同じようにタイルの間にできる柵も別の部屋扱いになりますが、タイルに置くタイプの柵は「柵の形をしたオブ」であって壁ではないので同じ部屋と認識されます。

helper2-8.jpg
ここまでくれば分かりますね。屋外で、Roomが0ではない椅子に座るようにすればベランダや柵に囲まれた庭の椅子に座ってくれます。具体的には、3行目のTrue/Falseを逆にしてしまえば終わりです。
始めはRoomの判定を無くせばいいと思ったんですが、どうやらRoom0の方が優先度が高いらしく、外にある遠くの椅子に座ろうとするのでこうなりました。なので、外(Room 0)にある椅子には座りません。

今回修正した BHAV #4114:"sit down" で座る椅子を決めているので、工夫すれば指定した椅子に優先して座るとかも可能です。取りあえず今回はここまでで。




ついでに、ヘルパーが椅子に座るときに行ったり来たりするのを直してしまいましょう。
helper2-4.jpg
こちらはヘルパー用に椅子を並べてあるところですが、左の椅子が空いているにもかかわらず、既に座っている椅子の前でウロウロし続けます。

helper2-9.jpg
原因は、4行目にある"Stack Object's Flags set? in use"です。
ここでは椅子が使用中かどうか調べているんですが、ここに落とし穴があります。
4行目は「使用中のフラグが立っているか」を調べていますが、実はこのフラグ、勝手にセットされるものではありません。使う時に、自分でセットするコードを書かなくては、使用中にならないわけです。
他のオブジェクトなどで、使う前に"(glob:)Standard Entry"、使い終わったら"(glob:)Standard Exit" など実行しているのを見たことがあるかもしれませんが、この中で"in use"フラグをセットしています。
椅子に関しては、座るときにフラグをセットしていないので、4行目の方法だと使用中と判断されずに行ったり来たりしてしまうわけです。


そこで、4行目を"Slot0 が空かどうか"という命令に変更します。Slotはそのオブジェクトに物を置いたりする場所で、テーブルの上とかシムの手とかそういう場所を表します。椅子のSlot0はいわゆる座る場所で、座っているときにはここにシムのIDが入っています。0なら誰も座っていないという事です。

これで椅子が使用中かどうか正しく判定できるようになるので、きちんと空いている椅子に座ってくれるようになります。



.

(SimPeople)ヘルパーに食器洗いだけをして欲しい

ウチは大家族なのでヘルパーを複数雇っていますが、ヘルパーに特定の行動のみをして欲しかったり優先してほしい時があります。食後は大量に食器が出るのにトイレや風呂の掃除にかかりっきりで、次の食事までに食器の片づけが間に合わなかったり。
そこで、掃除は食器洗い専門のヘルパーを作ってみたいと思います。食器洗いなら、PreSIMS様の「バスボーイ呼び出し植物」とかありますが、暇になったら水やりとか座ったりするヘルパーとして欲しかったので。
それ以外の仕事の優先順位も簡単なのでついでに触れておきます。
相変わらず横道にそれてばかりの解説ですが、まあそういうのも何かのヒントになるかと思いつつ。




さて、大抵のヘルパーでは1つの関数でどの仕事が優先かが決まっています。ヘルパー呼び出し絵画ベースならBHAV:#4111の"am I needed?"です。大概Mainから呼びだしているので、そちらを見ればそれっぽい名前の関数があるはずです。

washdish1.jpg
見ての通り、この順番に仕事をしていきます。"Repair"が(何も壊れてないとかで)失敗すると"Clean"で色々掃除を、それもなければ"Garden"で庭や畑へ、となっています。
この順番を入れ替えるだけで掃除優先とかにできます。掃除だけをしてほしいなら"Clean"以外を削除して、Cleanが成功/失敗したら"True"/"False"を返すようにするだけです。

でも、今回は掃除の中でも食器洗いを優先してほしいので、"Clean"の中身をいじることになります。




washdish2.jpg
こちらが"Clean"の中身です。0〜5行目当たりの処理は、"GUID:e12a1a9e"のオブジェクトがマップ内にあるかどうかで処理を分ける部分です。このGUIDはVacationメイドの物で、「Vacationメイドがいたら6行目、いなければ7行目」を実行します。
ここは家のヘルパーとしては意味がないので削除してもいいですし、「同じタイプのヘルパーが複数いたら、2人に1人は皿洗い専門」とするのも可能です。

washdish3.jpg
6、7行目の「Find Best Object for Function」命令は見ての通り、「Cleanするのに一番ふさわしいオブジェクト」を返してきます。パッと見だとこの2行は同じように見えますが、よく見るとHEXの7バイト目が違います。IFFPencil2が対応していないので左の表記は同じになっています。
検索ではSims2の物しか見つかりませんでしたが、検証の結果シムピでも同じ動作なのが判りました。ここが1だと「屋外の物」のみを対象にするようです。0だと無指定、検証していませんが2だと屋内の物のみになると思われます。
要するに、「Vacationメイドがいるなら外の仕事だけをする」となっています。まあ、今回のハックには関係ないんですが。




ここで一つポイントが。この「Find Best Object for Function」命令は、その名の通り「全オブジェクト中から一番ふさわしいものを返す」命令です。つまり、シム内の時間が経過しない(状況に変化がない)限り、何度呼び出しても同じオブジェクトが返ってきます。StackObjectがどのオブジェクトを示していても同じです。ぶっちゃけ、ここの4行目でStackObjectを0にしているのは全くの無意味です(多分)。

対して、オブジェクトの検索にはよく「Set to Next」命令が使われますが、こちらは「現在のStack Objectが示すオブジェクト以降のもので条件に合う物」を順番に返してきます。
似ているようで全く違うので気を付けてください。

さて、「何度呼び出しても同じオブジェクトが返ってくる」、これだけ聞くとどうしようもない気がします。"Set to Next"ですべてのオブジェクトから検索しないといけないんでしょうか。
単純に"Set to Next"を使うと、そのままだと自分からの距離など関係なくすべての条件に合うオブジェクトが出てきます。その中から、自分から近いものとか先に片付けるのに相応しいものを選ぶコードが必要になります。
例えばシムは「高級で自分に近い」椅子に座ろうとしますが、"Set to Next"で何も考えずに処理すると最後に設置した椅子から逆順に座っていきます。「高級で」「自分に近くて」などの処理を自分で書かなくてはなりません。
「Find Best Object for Function」だとその辺を勝手にやってくれるので、なるべく「Find Best Object for Function」を使いたいわけです。

大丈夫、ちゃんと回避する方法があります。それが、12行目にある「lockout count」です。
厳密に検証したわけでは無いので多少推測が混じりますが、オブジェクトの「lockout count」に指定したカウントが0になるまでの間、「Find Best Object for Function」の結果から除外(Lock out)する、という動作をするようです。
ここでのカウントは、よく使われる"(glob:) idle" と同じ単位で、ゲーム内時間の2秒で1つ減少します。
シムピープルの最小の時間単位は、ゲーム内時間で2秒です。ゲーム内時間で1分はリアル時間で大体1秒ですから、30フレーム/秒と考えて1フレーム単位で時間が進む、と言った方がいいかもしれません。

つまり、検索結果が食器以外だったらLockout Countを1以上に設定して再検索すれば、(存在していれば)いずれ食器が選択肢に出てくることになります。またほとんどの食器はCategoryが32なので、これで判断することが出来ます。掃除できるオブジェクトでCategoryが32なのは食器だけと決まっているので。一部(切り分けたケーキなど)は違うらしいですが。
「掃除できるオブジェクトでCategoryが32なのは食器だけと決まっている」と言いましたが、これはシステム的に決まっているのではなくて、「そういうルールで公式が作っているので、皆それにならっている」という、いわゆる「オブを作るときのお約束」です。Category32は装飾品ですが、もし装飾品枠でトイレを作るとおそらく誤爆して掃除に行きます。

ここでは、Lockoutしたオブジェクトを次のフレームにはLock解除したい(じゃないと、食器以外の掃除が滞る)ので、LockoutCountに1を指定しています。元のコードに"30"とあるのは、「何か理由があって今は片づけられないから、ゲーム内1分後以降に再挑戦して」という意味だと思われるので、こことは別にしておきます。

washdish4.jpg
以上をまとめると、このようなコードになります。「Categoryが32じゃなかったら、LockoutCountを1にして再検索」を行うコード2行を追加しただけです。
※ Constant 8198:31 は32です。

ヘルパーが余程たくさんいる状況だと、偶然同じフレームに動作が重なって掃除するものがあるのに休憩する、とかがあるかもしれません。まあ、それほど頻繁に起きることは無いはずですが。
どうしても気になるなら、オブジェクトを検索した後に「LockoutCountが1のオブジェクトを検索して、0にする」コードを追加すればいいでしょう。全オブジェクトを検索するので、多少負荷が増えることになりますが。

これで「食器洗い専門のヘルパー」の出来上がりです。逆に食器洗い以外を行うヘルパーも出来ますし、条件を変えれば色々できるでしょう。
まあ、どちらかというと「ヘルパーには色々やってほしい」という方がほとんどでしょうけど。

今回は2行追加しただけなので、特に配布とかはありませんので悪しからず。


.