top / 1 / 2 / 3 / 4 / ex1 / 5 / 6 / ex2 / 7 / 8 / 9 / 10 / 11 / ex3 / 12 / 13 /..../ 14 /..../ 関数 / 覚え書き / 倉庫
第13回 【 スクリプト型MODを作るA 】

「修理キットを作ってみる」

今回は修理キットの造り方を説明しようと思います。

あー、今回は我ながら酷いと思うほどに文章がグダグダしてます。いやグダグダしてるのはいつもの事ですが。
文章の99%において語尾が「〜ます。」と「〜です。」になってます。小学生の作文みたいです。
何故かと言うと、モニターが御臨終になって慌てて新しいの買ったらドット抜け大当たりでもう一度店に行ってゴネたら返品させてもらえたので(最低)、その後ドット抜け保証のあるネット通販を片っ端から当たって結局ツクモ電気に決めたのがついさっきの事で、えらく疲れてて日本語を整える気力がどうにも沸いてこないのです。
お見苦しいとは思いますが、そのうち整えようと思うんで御勘弁を。ハフゥ。´w`

知ってる方も居ると思いますが、オイラは以前に STALKER files 「Repair Kit with Choco Egg Mod」を公開しているのですが、今回の内容は公開済みのものと大幅に被ると思います。

なお、STALKER filesで公開している「Repair Kit with Choco Egg Mod」で追加される修理キットには自作アイコンも追加しましたが、今回は既存のグラフィックを流用して、文字を捏ね回すだけで作れるように説明していきたいと思います。
アイコン追加は恐らく痛銃の応用でいけると思いますが、そのうち順を追って説明したいと思います。

まずは構成から。

□repair_kit ┣□gamedata ┃┣□config ┃┃┣□misc ┃┃┃┗■items.ltx ┃┃┣□text ┃┃┃┗□eng ┃┃┃ ┗■string_table_ngcmod.xml(新規作成) ┃┃┗■localization.ltx ┃┗□scripts ┃ ┣■bind_stalker.script ┃ ┗■ngc_mod.script(以前作ったもの) ┗■readme.txt

かつてないややこしさになってる気もしますが、良く見るとそうでも無いです。
以前の「ゲーム開始時点で任意のアイテムを所持させる」で作った環境と、武器追加と同じような構成が加わっただけです。
あと、今回からは文字列テーブル「string_table_ngcmod.xml」を独自に用意するので、「localization.ltx」も修正する必要があります。


とりあえずアイテムを作りましょう。
基本的な方法は【 アイテムの追加の仕方 】と同じです。フォルダ「gamedata\config\misc」内のファイル「items.ltx」の最後に適当な食い物か薬をコピーして貼り付けましょう。
んで、ここで設定できる限りの事を設定しちゃいます。

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;@@@@ NGC_MOD ADDITIONAL Start @@@@ ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;@Repair Kit [repair_kit]:identity_immunities GroupControlSection = spawn_group discovery_dependency = $spawn = "devices\quest_items\repair_kit" $prefetch = 32 class = II_ANTIR cform = skeleton visual = equipments\item_merger.ogf radius = 1 description = enc_equipment_repairkit inv_name = repairkit inv_name_short = repairkit inv_weight = 0.02 quest_item = true inv_grid_width = 2 inv_grid_height = 1 inv_grid_x = 8 inv_grid_y = 18 cost = 0 eat_health = 0 eat_satiety = 0 eat_power = 0 eat_radiation = 0 eat_alcohol = 0 wounds_heal_perc = 0 eat_portions_num = 1 animation_slot = 4 ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;@@@@ NGC_MOD ADDITIONAL End @@@@ ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

この辺はそれほど迷いませんね。
「アイテムID」とか「description」「inv_name」などは今まで通り、任意の名前を付けます。

いくつかの項目、「GroupControlSection」「$prefetch」らへんは相変わらず良く判らないので、これを作る際に参考にした「ABC_MOD」「sleep_bag」に習って作りました。「$spawn」も今までと同じ要領で変えておきましょう。念のため。
「class」は他に体力回復が目的でない消費型アイテムも見当たらなかったので「II_ANTIR(放射能除去薬)」で良いと思います。

visual = equipments\item_merger.ogf inv_grid_width = 2 inv_grid_height = 1 inv_grid_x = 8 inv_grid_y = 18

この辺で見た目を弄ってます。
今回はCordon南の基地やAgroprom基地で手に入る白いカバンを流用しましょう。

quest_item = true

クエストアイテム扱いにしてあるのには事情があります。
これには経緯が少々あるので、後ほど詳しく説明しますね。

eat_health = 0 eat_satiety = 0 eat_power = 0 eat_radiation = 0 eat_alcohol = 0

体力回復もなし。
あとは他のアイテムでも殆ど同じ設定なので、そのまんま突っ込んでおきます。


アイテムの骨組みが出来たので、今度はまた文章を作りましょう。
フォルダ「gamedata\config\text\eng」内に新規作成したファイル「string_table_ngcmod.xml」に、XML形式で記述します。

<string_table> <string id="enc_equipment_repairkit"> <text>This portable repair kit can fix your weapon and armors anywhere of zone.</text> </string> <string id="repairkit"> <text>Repair Kit</text> </string> </string_table>

こんなもんでしょうか?
今回は新規作成したXMLファイルをゲームに認識してもらうために、フォルダ「gamedata\config」内のファイル「localization.ltx」を修正します。
開いてみると、後ろの方にXMLファイルの名前がずらずらっと並んでますね。
その最後に、

, string_table_ngcmod

これを追加するだけです。
この指定により、ゲームはIDから文章を探す際にこのXMLファイルも見に行ってくれるようになります。

とりあえずアイテムの方はこれくらいで良いでしょう。
後で必要があったらちょこちょこ修正するかも知れません。

ちなみに、アイテムの追加自体はこれで完了です。
この時点で一度動作確認した方が良いかもしれません。
まだ修理機能を追加していないので修理キットとしては機能せず、使っても何も起こらない無意味アイテムとして登場するはずです。
前に作ったスクリプトにこんな感じでアイテム追加してやれば、すぐに確認できます。

alife():create("repair_kit", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id())

このスクリプト、痛銃作ったときの確認とかにも使えるので何気に便利ですね。
動きましたか?
それでは、いよいよ修理機能を追加しましょう。


手順は前のスクリプトを作ったのと同じ要領で行きます。
まず、フォルダ「gamedata\scripts\」内のファイル「bind_stalker.script」を開きます。
前と同じように「主人公に関する何らかのタイミング」をトリガーにするのですが、アイテムに関するトリガーのファンクションはこれくらいしかありません。

actor_binder:on_item_take(アイテムを取得した時)
actor_binder:on_item_drop(アイテムを失った時)
actor_binder:take_item_from_box(アイテムを箱から手に入れた時)
actor_binder:on_trade(トレードした時)

本来ならば「アイテムを使用した場合」と言うトリガーが欲しいところですが、これしかないので、この中で選ぶしかないのです。
選ぶも何も、「actor_binder:on_item_drop(アイテムを失った時)」しかありませんね。
幸いにも、ファクション「actor_binder:on_item_drop」は、「アイテムを捨てた時」ではなく「アイテムを失った時」に動作します。つまり、消費する事でアイテムを失っても動作するわけです。
現在、アイテム消費をトリガーにスクリプトを動かす方法は他には無い模様です。
実はここで先ほど「items.ltx」で設定した項目、

quest_item = true

これが関係してきます。
このファクション「actor_binder:on_item_drop」は、もちろんアイテムを「捨てた」時にも動作してしまいます。
つまり、アイテムを消費した場合に修理機能が働くのに加えて、アイテムを捨てた時にも動作してしまうのです。
捨てた以上、足元に落ちてるわけで、それを再度拾う事で何度でも使えてしまうわけですね。
この現象の回避策として、そもそも「Drop」を出来ないようにしたと言うわけです。
Drop出来ないようにする方法として、クエストアイテム扱いにしたわけですが、弊害として、

・売買できない
・消費する以外に手放す方法が無い


この2点が残ってしまいました。
この後、もっと便利な項目があることを知り、今後はそちらの方へと移行していこうと考えています。
ちなみにその項目とは、これです。

auto_attach = false

クエストアイテム扱いにする代わりにこの項目を「false」に設定することで、単に「捨てる事ができなく」なります。
クエストアイテム扱いにしないで済むと言う事は、売買可能になる事を意味しています。汎用性としては、こちらの方が優れていますね。
ただし、クラスが「II_FOOD」「II_MEDKI」だと捨てる事が出来る場合があるようです。ここら辺、法則がイマイチ掴みきれてません。とりあえず「II_ANTIR」にしておけば、捨てる事はできなくなるのでこれにしてます。
まあ、この辺は後で各自修正してもらうとして、今は取りあえず「auto_attach」の設定は行わず、クエストアイテム扱いと言う事で話を進めさせてもらいますね。


さて、ファンクション「actor_binder:on_item_drop」を改造しましょう。
「bind_stalker.script」内、184行目らへんにこんなのがあります。

function actor_binder:on_item_drop (obj) level_tasks.proceed(self.object) --game_stats.update_drop_item (obj, self.object) end

これが「アイテムを失った時」の処理です。
ここに施す修正も前と同じく1行。

ngc_mod.itemuse(obj) --NGC_MOD ADDITIONAL

これを問題のファンクションの最終行に突っ込みましょう。

function actor_binder:on_item_drop (obj) level_tasks.proceed(self.object) --game_stats.update_drop_item (obj, self.object) ngc_mod.itemuse(obj) --NGC_MOD ADDITIONAL ←これを突っ込んだ。 end

こうです。簡単ですね。
これでファイル「bind_stalker.script」への修正は終了です。


さあ、いよいよ本番ですよ。
またゼロからスクリプトを生み出す時間がやってまいりました。この部分が一番ワクワクします。
前回作ったファイル「ngc_mod.script」を開いてください。

ファンクション名は「itemuse」でしたね。
オイラが偉そうに言っている命名規則「固有名詞を入れたほうが良い」に反していますね。はい。その通りです。
恥ずかしい話ながら、この頃にはまだスクリプトを作るのに慣れてなくて、命名も適当にやってしまったのです。
(次の改版にあわせてこっそり直しちまおう・・・)

余談ですが、ファンクションを呼び出す際には「ngc_mod.ngclordcheck」のように「ファイル名.ファンクション名」となってますよね。
これは住所とかを示すのと同じで「ngc_mod.」と書いた時点でそのファイル以外を見に行く事はないようです。
多分、ファイル「ngc_mod.script」内だけを探すので、自作ファイル内のファンクション名では「使われる可能性の高い名前」を使っても問題ないのではないかと思います。

が、それを確認する術も無く、このゲームの駆動プログラムがどんな動きをしているのか全く分かっていない以上、どんな意外な動作をするやも知れません。
だから、確実に誰も使わない名前を付けた方が万が一の誤動作を避けられるんじゃないかなと思って、あえて「固有名詞を」とお勧めしている次第なのです。
一般的な命名規則としてもこの方が理に適ってると言うものありますしね。
そんなわけで、やはり今後は出来る限り固有名詞付きでいきますね。
余談終わり。

では、「itemuse」を書きましょう。
書き方は前と同じです。まずは第一段階。

function itemuse(what) end

ここでは引数として「what」を受け取っていますね。
このwhatには、呼び出し元である「actor_binder:on_item_drop」において「obj」と言う変数で扱われている「何か」が渡されています。
えー、結論から言うと、アイテム情報です。
まあ「actor_binder:on_item_drop」自体が「アイテムを失った時に実行される処理」なわけだから、他に何があるんじゃと言う話ですが、とにかくアイテム情報なんです。
この辺はMOD「ABC Sleepbag」などを解析して何となく体得したものなので、そーゆーものと思っておいた方がスッキリすると思います。

そのアイテム情報ですが、これは構造体で、複数の変数や関数で構成されています。
その関数群の中に「name()」と言うヤツがありまして、こいつを実行すると、戻り値として「アイテムID」を返してくれます。
構造体の関数を実行するのは、こうですね。

local obj_name = what:name()

あ、面倒なので変数「obj_name」の定義を同時にやっちゃってます。
この記述方法もやたらに目にします。特に定義した変数を初期化する場合に多用されてますね。

これで変数「obj_name」の中に捨てたアイテムのアイテムIDが入ったわけですが、それを確認するのは、この関数です。

string.find(obj_name, "アイテムID")

ここではアイテムIDに、さっき「items.ltx」内に追加したアイテムID「repair_kit」を指定します。
実行すると戻り値としてフラグを返してもらえます。
「true」なら一致、「false」なら不一致、関数だけ見ると「obj_nameの中に文字列"repair_kit"が無いか探せ」と言う感じで、何だか判りにくいけど、どうやらアイテム名を確認するのはこの関数で行うのがSTALKER MODの中では常識らしいです。
これも理由とかは良く判りません。普通に「obj_name == "アイテムID"」でも判定できそうな気もしますが、上手く動きませんでした。だから「そーゆーもの」で納得してください。実に頼りないですね。ハイ。すいません。

さて、これをIF条件式に組みこむと、こうなりますね。

if (string.find(obj_name, "repair_kit") == true) then アイテムIDが"repair_kit"であった時に行う処理 end

ちなみにちょっとした文法の薀蓄ですが、上記の様な「== true」の記述は省略できます。
比較演算子が「==」であり、結果が「true」の時だけ使える省略です。
実際に書くと、こうです。

if (string.find(obj_name, "repair_kit")) then アイテムIDが"repair_kit"であった時に行う処理 end

この記述でも前述したものと全く同じ動作をします。
例外的な省略方法ですが、かなり多く見かける記述なので覚えておいた方が良いと思います。


さて、いよいよ修理キットの真髄に迫ります。
まずは、修理対象のアイテム情報を取得する必要があります。その関数はこれ。

db.actor:item_in_slot(装備スロット)

装備スロットには数値が入り、以下のように対応してます。
1:サブ武器
2:メイン武器
6:防具


これを実行すると、戻り値としてアイテム情報の構造体を返してくれます。
つまり、実際に使う時には、

local item_in_slot = db.actor:item_in_slot(1)

こんな感じです。
スロット番号が1なので、これはサブ武器の情報ですね。
実際にはサブ、メイン、防具の3つのアイテム情報が欲しいので、

local item_in_slot_1 = db.actor:item_in_slot(1) local item_in_slot_2 = db.actor:item_in_slot(2) local item_in_slot_6 = db.actor:item_in_slot(6)

こうなりますね。

さあ、次行きます。
たった今取得したアイテム情報より、そのアイテムの耐久度を取得する方法です。
関数はこう。

アイテム情報:condition()

上記の例で取得したアイテム情報を使う場合、こんなんなります。

item_in_slot_1:condition() item_in_slot_2:condition() item_in_slot_6:condition()

引数はありません。
これを実行すると、戻り値として現在の耐久度を返してもらえます。
耐久度は「0.01=1%」として、「0.01〜1.00」の形式で現されます。つまり防具が20%分のダメージを受け、80%まで低下してしまっていた場合、「0.80」と言う値になるわけです。

次、アイテムの耐久度を変更する関数です。
これが修理キットのメイン部分と言えるでしょうか?

アイテム情報:set_condition(設定したい耐久度)

「アイテム情報」は今までどおり、「設定したい耐久度」には「0.00〜1.00」の範囲で希望の数値を設定します。
つまり、サブ武器を耐久度50%にしたい場合、

item_in_slot_1:set_condition(0.50)

こうです。戻り値はありません。
気をつけるべきことは、これを実行すると、「耐久度が50%回復するのではなく、耐久度が50%になる」と言う事です。
回復ではなく、あくまでも変更なのです。
変更ではなく回復させたいときには、「現在の耐久度+回復させたい耐久度」を設定してやる必要があるのです。
つまり、サブ武器を例にすると、

local rep_point = item_in_slot_1:condition() + 0.2

こうやって、「現在の耐久度+20%」の数値を変数「rep_point」に設定して、

item_in_slot_1:set_condition(rep_point)

これでサブ武器の耐久度を「20%回復」させる事が出来るわけです。
ここまでをまとめると、

function itemuse(what) if (string.find(obj_name, "repair_kit") == true) then -- 設定するべき耐久度rep_pointを定義、ゼロで初期化 local rep_point = 0 -- 3つのスロットのアイテム情報を取得 local item_in_slot_1 = db.actor:item_in_slot(1) local item_in_slot_2 = db.actor:item_in_slot(2) local item_in_slot_6 = db.actor:item_in_slot(6) -- サブ武器の耐久度を20%回復させる。 rep_point = item_in_slot_1:condition() + 0.2 item_in_slot_1:set_condition(rep_point) -- メイン武器の耐久度を20%回復させる。 rep_point = item_in_slot_21:condition() + 0.2 item_in_slot_2:set_condition(rep_point) -- 防具の耐久度を20%回復させる。 rep_point = item_in_slot_6:condition() + 0.2 item_in_slot_6:set_condition(rep_point) end end

とりあえず荒削りの原型ではありますが、最低限の機能は確保できました。
この状態で修理キットを使うと、全ての装備アイテムの耐久度が20%回復する事になります。
こんな大味なままでは、お世辞にもスマートとは言えないので、もっと機能を洗練させますが、その前にやる事が2つあります。
一つは「修理キット以外のアイテム対策」
もう一つは「例外処理(フェールセーフ)」です。
順番に説明しますね。

まずは「修理キット以外のアイテム対策」から。
今回追加するのは修理キットだけなんで、たった今絶対に必要と言うわけではありませんが、この先もスクリプト発動タイプのアイテムを追加したくなる事もあると思います。
その度にIF条件式をダラダラと追加していくと非常に見難くなり、デバッグも困難になります。
そこで、関数「itemuse」を更に細分化し、処理を別々に記述できるようにします。

function itemuse(what) if (string.find(obj_name, "repair_kit") == true) then end end

まず、ここまでは今までどおり。
ここで、新しい関数を造ります。
名前は・・・修理キットを使った場合の処理なので、「use_repair_kit」とでもしておきましょう。
それを上記のIF条件式から呼び出します。

function itemuse(what) if (string.find(obj_name, "repair_kit") == true) then use_repair_kit(what) end end

更に、さきほど書いた修理機能の本体を新しく作った関数「use_repair_kit」として記述します。

function use_repair_kit(what) local rep_point = 0 local item_in_slot_1 = db.actor:item_in_slot(1) local item_in_slot_2 = db.actor:item_in_slot(2) local item_in_slot_6 = db.actor:item_in_slot(6) rep_point = item_in_slot_1:condition() + 0.2 item_in_slot_1:set_condition(rep_point) rep_point = item_in_slot_21:condition() + 0.2 item_in_slot_2:set_condition(rep_point) rep_point = item_in_slot_6:condition() + 0.2 item_in_slot_6:set_condition(rep_point) end

こんなふうに、二つの関数に分けました。
何故このような事をするのかと言うと、例えばこの先、「choco_egg」なるアイテムを追加して、それを使用した際に何らかのスクリプトを動かすものとします。
その時には、

function itemuse(what) if (string.find(obj_name, "repair_kit") == true) then use_repair_kit(what) elseif (string.find(obj_name, "choco_egg") == true) then use_choco_egg(what) end end

こんな感じで分岐させる事で、機能ごとに別々の関数を作れるのです。
これにより見やすくなるのもメリットですが、問題が発生した時に巨大な関数をダラダラと追わずに新たに作った関数だけデバッグすれば良くなるのです。
スクリプト型アイテムを増やす度に、elseif文を追加していけば、いくらでも増やす事は出来ます。


次は、「例外処理(フェールセーフ)」です。
フェールセーフとは、「fail safe」つまり失敗時の復旧処理と言うような意味です。

先ほどまとめた「全ての装備アイテムの耐久度が20%回復するサンプル」ですが、実は、あれを実行すると高い確率でクラッシュします。
その理由は、もしも何も装備していないスロットが一つでもあったら、存在しないアイテムの耐久度を取得したり設定したりしようとしてしまうからです。
このような「プログラム的に矛盾した事態が発生した時」には高確率でクラッシュしてしまうのです。
そこで、事前にクラッシュしないように対策をしておく必要があります。それがフェールセーフです。

今回の例では「アイテム情報が“空っぽ”だった場合には処理をしない」と言う事で回避できます。
アイテム情報が空っぽであるかどうかは、このIF条件式で判定します。

if (アイテム情報 ~= nil) then

「nil」と言うのが「空っぽ」を意味していると考えてください。
つまり、「~= nil」とは「空っぽでなければ」と言う意味になりますね。
サブ武器を例にすると、

-- サブ武器を装備しているか? if (item_in_slot_1 ~= nil) then rep_point = item_in_slot_1:condition() + 0.2 item_in_slot_1:set_condition(rep_point) end

こんな感じですね。
これで、サブ武器を装備していないのに修理キットを使用すると何もしないで終了する為、クラッシュを回避する事が出来るのです。

さて、実はまだ問題があります。
アイテムの耐久度ですが、これは「1.00」が最高値ですね。
もしもアイテムがまだ殆ど新品同様で、耐久度が「0.99」だったりしたら、これに「0.20」を加えると「1.19」になってしまいます。
実はこの数値をぶち込んだらどうなるかは、まだ試していません。いませんが、ロクな事にならないのは容易に想像できますね。
なので、これもフェールセーフしちゃいます。
もしも「0.20」を足した結果が「1.00」を超えるのであれば、有無を言わさず「1.00」に補正するように改造します。

-- サブ武器を装備しているか? if (item_in_slot_1 ~= nil) then rep_point = item_in_slot_1:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_1:set_condition(rep_point) end

こうです。
これで、今の時点で考えられるプログラム的な矛盾は回避できるはずです。
まとめると、

function itemuse(what) if (string.find(obj_name, "repair_kit") == true) then use_repair_kit(what) end end function itemuse(what) local rep_point = 0 local item_in_slot_1 = db.actor:item_in_slot(1) local item_in_slot_2 = db.actor:item_in_slot(2) local item_in_slot_6 = db.actor:item_in_slot(6) -- サブ武器を装備しているか? if (item_in_slot_1 ~= nil) then rep_point = item_in_slot_1:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_1:set_condition(rep_point) end -- メイン武器を装備しているか? if (item_in_slot_2 ~= nil) then rep_point = item_in_slot_2:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_2:set_condition(rep_point) end -- 防具を装備しているか? if (item_in_slot_6 ~= nil) then rep_point = item_in_slot_6:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_6:set_condition(rep_point) end end

とりあえず、これでいきなりクラッシュする事はなくなりました。
一通り、最低限の修理機能は実現できたので、動作確認して見る事をお勧めします。
ちなみに、MODを作っていると、思わぬところで、思わぬ原因でクラッシュしまくるものです。
そんな時にも慌てずに、第9回 【 デバッグ方法 】で説明したエラー情報を追う事で原因が判るはずなので、そのプログラム的矛盾を回避するIF条件式を組み込みましょう。


さて、ここまでで修理機能の基本は出来たわけですが、実際にはここからが本当の本番です
貴方のセンスが問われる時です。

今回作った修理キットは一応機能はしますが、正直、ダサイと思いませんか?
1個の修理キットを使用しただけで、装備中のアイテム全てが20%ずつ回復するとか、全然現実的じゃないし、なによりもゲーム的にシラけてしまいます。
そこで、ここまでに覚えた文法や記述を駆使して、この修理キットを更に現実的なものに、もっとゲームが面白くなるように改造しましょう。

正直、ここがMOD製作で最も楽しく、最も悩むところです。オイラはこの楽しみのためにMODを造っていると言っても過言ではありません。
さて、修理キットの動きを現実的にするには、色々と方法が考えられます。
例えば・・・、

1. アイテムを「repair_kit_sub」「repair_kit_main」「repair_kit_armor」の3つに増やして、それぞれ、対応した装備しか回復しないようにする。
2. アイテムの回復度合いを ランダム にする。
3. 一番消耗の激しいアイテムを優先して回復するようにする。


これらを組み合わせるのでも良いし、他にも考えれば色々とアイディアはあると思います。
オイラは最初は2番で作ったんだけど、イベントリの中身がゴッチャゴチャになっちゃって、とてもスマートでない印象になってしまったので、結局3番に落ち着きました。

ダラダラ解説するのも何なので、一気に行きますね。
・・・こうです。

function use_repair_kit(what) local repair_slot_num = 0 local item_in_slot_1 = db.actor:item_in_slot(1) local item_in_slot_2 = db.actor:item_in_slot(2) local item_in_slot_6 = db.actor:item_in_slot(6) if (item_in_slot_1 ~= nil) then repair_slot_num = 1 end if (item_in_slot_2 ~= nil) then if (repair_slot_num == 0) then repair_slot_num = 2 elseif (repair_slot_num == 1) then if (item_in_slot_1:condition() > item_in_slot_2:condition()) then repair_slot_num = 2 end end end if (item_in_slot_6 ~= nil) then if (repair_slot_num == 0) then repair_slot_num = 6 elseif (repair_slot_num == 1) then if (item_in_slot_1:condition() > item_in_slot_6:condition()) then repair_slot_num = 6 end elseif (repair_slot_num == 2) then if (item_in_slot_2:condition() > item_in_slot_6:condition()) then repair_slot_num = 6 end end end if (repair_slot_num == 1) then local rep_point = item_in_slot_1:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_1:set_condition(rep_point) elseif (repair_slot_num == 2) then local rep_point = item_in_slot_2:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_2:set_condition(rep_point) elseif (repair_slot_num == 6) then local rep_point = item_in_slot_6:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_6:set_condition(rep_point) end end

ちょっと長いですね。
日本語で書くと、こうです。

function use_repair_kit(what) 修理スロット番号を定義、ゼロで初期化 サブ武器のアイテム情報取得 メイン武器のアイテム情報取得 防具のアイテム情報取得 if (サブ武器スロットが「空っぽ」でなければ) then とりあえず修理スロット番号を「サブ武器」にする。 end if (メイン武器スロットが「空っぽ」でなければ) then if (修理スロット番号がまだゼロのままならば) then 修理スロット番号を「メイン武器」にする。 elseif (修理スロット番号が「サブ武器」だったら) then if (サブ武器よりメイン武器の消耗が激しいなら) then 修理スロット番号を「メイン武器」にする。 end end end if (防具スロットが「空っぽ」でなければ) then if (修理スロット番号がまだゼロのままならば) then 修理スロット番号を「防具」にする。 elseif (修理スロット番号が「サブ武器」だったら) then if (サブ武器より防具の消耗が激しいなら) then 修理スロット番号を「防具」にする。 end elseif (修理スロット番号が「メイン武器」だったら) then if (メイン武器より防具の消耗が激しいなら) then 修理スロット番号を「防具」にする。 end end end if (修理スロット番号が「サブ武器」だったら) then 現在耐久度+20%を暫定耐久度として設定 if (暫定耐久度が100%超えてしまったら) then 100%を暫定耐久度として設定 end 暫定耐久度の値で現在耐久度を書き換える。 elseif (修理スロット番号が「メイン武器」だったら) then 現在耐久度+20%を暫定耐久度として設定 if (暫定耐久度が100%超えてしまったら) then 100%を暫定耐久度として設定 end 暫定耐久度の値で現在耐久度を書き換える。 elseif (修理スロット番号が「防具」だったら) then 現在耐久度+20%を暫定耐久度として設定 if (暫定耐久度が100%超えてしまったら) then 100%を暫定耐久度として設定 end 暫定耐久度の値で現在耐久度を書き換える。 end end

とりあえず、これで最も消耗の激しいアイテムを優先して修理するような機能になりました。
もちろん、修理するアイテムを自分で決めたい場合には、それ以外の装備を外せば良いようになってます。
オイラ的には修理キットとかはケチって使うことが多い(と思う)ので、終盤で余りまくる事を想定し、「連打で使用したら一気に全装備品を回復できる」と言うような仕様にしました。スペースを取らず、使い勝手が中々良いので何気に自信作ですw

ちなみに、実はこのスクリプトはかなり簡略化して書く事が出来ます。
このスクリプトを書いた時点ではデバッグ方法とかあまり理解していなかったため、「とにかく絶対にクラッシュしない」事を最大の目的として、最もシンプルで間違いのない記述を心がけました。
図らずも、ここで説明するのに良い材料になったので結果オーライってカンジですね。

あ、あとアイテム詳細のXML文章もこれに併せて変えておきましょう。
別に動作そのものには関係ないけどね。

ところで、このMODは「Gドライブとか、わけ判んない場所にSTALKERをインストールした環境」ではクラッシュする事があるようです。
海外ユーザーからの報告で、すげえ泣きそうになって調べてるのですが、何せオイラの環境は超シンプルなもんで、再現できませぬ。
つーか、ンなわけ判んねえ環境でやるなYO! TДTノ
何か、手掛かり掴めた人いたら教えてくだされ。マジお願い申す。


追記。原因判ったかも。
何度かやり取りして動作環境を良く聞いてみると・・・ドイツ人でした
「engフォルダ」を使用しているのが原因っぽいです。(ドイツ語版は「gerフォルダ」

先に言え


長くなりましたが、まとめますね。


構成:
□repair_kit ┣□gamedata ┃┣□config ┃┃┣□misc ┃┃┃┗■items.ltx ┃┃┣□text ┃┃┃┗□eng ┃┃┃ ┗■string_table_ngcmod.xml(新規作成) ┃┃┗■localization.ltx ┃┗□scripts ┃ ┣■bind_stalker.script(以前修正したものでも可) ┃ ┗■ngc_mod.script(以前作ったもの) ┗■readme.txt


追加アイテム:
ファイル:items.ltx ファイルの最後に以下の記述を追加 ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;@@@@ NGC_MOD ADDITIONAL Start @@@@ ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;@Repair Kit [repair_kit]:identity_immunities GroupControlSection = spawn_group discovery_dependency = $spawn = "devices\quest_items\repair_kit" $prefetch = 32 class = II_ANTIR cform = skeleton visual = equipments\item_merger.ogf radius = 1 description = enc_equipment_repairkit inv_name = repairkit inv_name_short = repairkit inv_weight = 0.02 quest_item = true inv_grid_width = 2 inv_grid_height = 1 inv_grid_x = 8 inv_grid_y = 18 cost = 0 eat_health = 0 eat_satiety = 0 eat_power = 0 eat_radiation = 0 eat_alcohol = 0 wounds_heal_perc = 0 eat_portions_num = 1 animation_slot = 4 ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ;@@@@ NGC_MOD ADDITIONAL End @@@@ ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


アイテム名、説明文:
ファイル:string_table_ngcmod.xml(新規作成) 以下の内容で新規作成 <string_table> <string id="enc_equipment_repairkit"> <text>This portable repair kit can fix your weapon and armors anywhere of zone. This kit fix most damaged item. If you would like to select fix item, I suggest about remove other equipped items.</text> </string> <string id="repairkit"> <text>Repair Kit</text> </string> </string_table>


追加XMLファイルの指定:
ファイル:localization.ltx 後ろの方に並んでいるXMLファイル名の最後に以下のファイル名を追加。 , string_table_ngcmod


既存スクリプト修正1:
ファイル:bind_stalker.script ファンクション:actor_binder:on_item_drop 最終行に以下の一文を追加。 ngc_mod.itemuse(obj) -- NGC_MOD ADDITIONAL


既存スクリプト修正2:
ファイル:ngc_mod.script(以前作ったもの) ファイルヘッダを以下のように修正。 --@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ --@@ NGC_MOD --@@ --@@ Function Now: --@@ 1. Item appears at start game. --@@ 2. Repair Kit --@@ --@@ Last Update:2008/02/15 --@@ Maked by Nyagicha --@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 最終行に以下の記述を追加。 ------------------------------------------------------------------------ --- Name : itemuse --- IN : what (item information) --- OUT : none --- Trigger : bind_stalker.script/actor_binder:on_item_drop/last line. --- Description : Trigger is item drop (not only drop but also use). --- If it is no disignated item, this function doesn't everything. ------------------------------------------------------------------------ function itemuse(what) local obj_name = what:name() if (string.find(obj_name, "repair_kit")) then use_repair_kit(what) -- If you want to add new trigger item, add [elseif] script this line. end end ------------------------------------------------------------------------ --- Name : use_repair_kit --- IN : what (item information) --- OUT : none --- Description : Proccess for repair kit ------------------------------------------------------------------------ function use_repair_kit(what) local repair_slot_num = 0 local item_in_slot_1 = db.actor:item_in_slot(1) local item_in_slot_2 = db.actor:item_in_slot(2) local item_in_slot_6 = db.actor:item_in_slot(6) if (item_in_slot_1 ~= nil) then repair_slot_num = 1 end if (item_in_slot_2 ~= nil) then if (repair_slot_num == 0) then repair_slot_num = 2 elseif (repair_slot_num == 1) then if (item_in_slot_1:condition() > item_in_slot_2:condition()) then repair_slot_num = 2 end end end if (item_in_slot_6 ~= nil) then if (repair_slot_num == 0) then repair_slot_num = 6 elseif (repair_slot_num == 1) then if (item_in_slot_1:condition() > item_in_slot_6:condition()) then repair_slot_num = 6 end elseif (repair_slot_num == 2) then if (item_in_slot_2:condition() > item_in_slot_6:condition()) then repair_slot_num = 6 end end end if (repair_slot_num == 1) then local rep_point = item_in_slot_1:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_1:set_condition(rep_point) elseif (repair_slot_num == 2) then local rep_point = item_in_slot_2:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_2:set_condition(rep_point) elseif (repair_slot_num == 6) then local rep_point = item_in_slot_6:condition() + 0.2 if (rep_point > 1) then rep_point = 1 end item_in_slot_6:set_condition(rep_point) end end


おまけ:
動作確認用に、以前に作ったスクリプトを修正。
ゲーム開始時に修理キットを大量に持ってるようにする。

ファイル:ngc_mod.script(以前作ったもの) ファンクション:ngclordcheck() ゲーム開始時の処理に以下の記述を好きなだけ追加。 alife():create("repair_kit", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id())


今回作ったもの:repair_kit.zip



top / 1 / 2 / 3 / 4 / ex1 / 5 / 6 / ex2 / 7 / 8 / 9 / 10 / 11 / ex3 / 12 / 13 /..../ 14 /..../ 関数 / 覚え書き / 倉庫
ニャギ茶★SPOT --工作部屋--