「逆引きリファレンス」の編集履歴(バックアップ)一覧はこちら

逆引きリファレンス」(2024/02/10 (土) 18:07:34) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

この項目ではやりたいことから調べられます。 やり方は一例であって必ずしもということはないので、他のパターンもあればぜひ記載してください。   #contents *ゲーム開始時に魔法やアイテムを自動で追加したい(初期化クエスト) MOD導入後に自動的にカスタマイズメニューの魔法やアイテムを追加するMODがあります。それのやり方です。 MOD導入後にセーブを読み込んだあとに一回しか動かないクエストです。 例としてカスタマイズメニューの魔法をプレイヤーに追加するクエストを作ります。 まず、Object WindowのCharacterツリーのQuestを選択します。 AchievementsQuestというのがあるので右クリック->Duplicateでコピー。 新規作成だとうまくいかない場合もあるので使えるクエストをコピーします。 コピーしたクエストを開いてIDと名前をつけます。(例:InitQuest) Priorityは99など高い数値にしておきます。 Start Game EnabledとRun at Onceにチェック。 そのままScriptsのボタンに移動して、要らないスクリプトをすべてRemove。 Addボタン->[New Script]でスクリプト新規作成。 extendsはQuest。 Spell property CustomMenuSpell(魔法名) auto Event oninit() Actor player = game.getplayer() if !(player.hasspell(CustomMenuSpell)) player.addspell(CustomMenuSpell) self.stop() endif endEvent Oninitは初期化した時に駆動するイベントです。 Oninitは同タイミングで二回読むっぽいので、重複防止のためにCustomMenuSpellを持っていない場合でしかプレイヤーに魔法を追加しません。 Run at Onceのため一度しか起動しないですが、念のためself.Stop()でクエストを止めます。 oninitはActorにつけても動きませんクエストで駆動するようにしましょう。 **ゲームロード度に動くスクリプト Oninitだと一回だけで、OnLoadは正しく動作しないのでOnPlayerLoadGameを使います(skyrim1.6以上必要)。 ただこれはActorのPlayerにしか返さないので、直接QuestにスクリプトをつけるのではなくAlias(エイリアス)を経由させて使います。 まず、Object WindowのCharacterツリーのQuestを選択します。 AchievementsQuestというのがあるので右クリック->Duplicateでコピー。 コピーしたクエストを開いてIDと名前をつけます。(例:LoadQuest) Priorityは99など高い数値にしておきます。 Start Game Enabledにチェックが入ってるか確認。 そのままScriptsのボタンに移動して、スクリプトをすべてRemove。 Addボタン->[New Script]でスクリプト新規作成。 extendsはQuest。例ではTestLoadScriptと名前をつけました。 Scriptname TestLoadScript extends Quest Event OnInit() ; 自前の関数 LoadFunc() EndEvent ;実際の処理はここに書く Function LoadFunc() debug.notification("Hello Work!") EndFunction コンパイルしてスクリプトのウィンドウを閉じる。 Quest Aliasesボタン->右クリック->New Referense Alias Aliasはplayerとでもしておく。 Unique ActorからPlayerを選ぶ。 ScriptのところをAddボタン->[New Script]でスクリプト新規作成。 TestLoadScript Property QuestScript Auto ; ↑QuestScript名 Event OnPlayerLoadGame() QuestScript.LoadFunc() EndEvent コンパイルしてスクリプトのウィンドウを閉じる。 Propertyで当クエストの指定も忘れずに。 *常時稼動させるスクリプト &bold(){※できる限り使わないことを考えぬいてください。} 常時稼働する事自体スクリプト(Papyrus)かそうでないかにかかわらず負荷がかかるために避けたほうが良いです。 とくに&bold(){間隔の短い}OnUpdateによるスクリプト回しは、環境にもよりますがスタックエラーにつながりやすいです。 OnUpdateとRegisterForSingleUpdate()を使います。 RegisterForUpdate()はスタックエラーの原因になりやすいので使いません。 つけたり止めたりしやすいのでクエストかマジックエフェクトで回します。 クエストの作り方は上記2つに書いてあるので省略。 例はプレイヤーがスニーク中かつ灯火使用中の場合は灯火を消します。 ;(マジックエフェクトの場合はEvent OnEffectStart) Event Oninit() RegisterForSingleUpdate(10) EndEvent Event OnUpdate() ;実際の処理はここ player = game.getplayer() if (player.isSneaking()) && (player.HasSpell(Candlelight)) DispelSpell(Candlelight) endif ;10秒後OnUpdate開始。つまり10秒単位で回る。 RegisterForSingleUpdate(10) EndEvent に1秒など短い間隔でループさせると大量にスタックしてCTDの要因や他のスクリプトの遅延になります。 (OnUpdate内の記述によりますが)できれば2秒以上ゆとりを持って設定しましょう。 高速でループさせる必要があるかどうかをよく吟味して、何か単発のイベントで代替できるか探りましょう。 *DLCや特定のespが読み込まれてる場合に処理をする GetFormFromFile()だと存在しない場合にログにエラーが出てしますのでGetModByName()を使います。 例ではドーンガード。 if Game.GetModByName("Dawnguard.esm") < 255 ; ドーンガードがある場合の処理。別のespのフォームIDからFormを取得するには Game.GetFormFromFile(0x00000000,"Dawnguard.esm") else ;GetModByNameが255を返した場合読み込まれていません ;ドーンガードがない場合の処理 endIf *一度のみ実行 Oninit()などで絶対に一度のみしか走らないで欲しい処理などに Bool doOnce = False Event Oninit() if (doOnce) return else doOnce = True endif ;実際の処理はここ EndEvent *名前 **アクターの名前の取得 対象アクターの名前を取得して左上に表示させたい時に、 debug.notification("ActorName:" + Actor) とやってもスクリプト名やActorと表示されるだけなので、以下のようにアクターベースから名前を取得します。 debug.notification("ActorName:"+ Actor.GetActorBase().GetName()) #highlight(linenumber,php){{ Actor actTarget; String szName; ; 現在のゲーム上で表示されている名前 szName = actTarget.GetDisplayName(); ; 表示名の変更に影響されない元の設定名 ; レベルドリストから生成されたアクターの場合、空白になる場合がある。 szName = actTarget.GetActorBase().GetName(); ; 表示名の変更に影響されない元の設定名 ; レベルドリストから生成されたアクターの場合でも、元になったActorBaseの名前が入る。 ; 通常のアクターの場合でも使えるので、設定名を取得する場合はこの方が確実。 szName = actTarget.GetLeveledActorBase().GetName(); }} **場所の名前の取得 場所の名前をセル→ロケーション→ワールドの順に取得する。 屋外などでは、セルやロケーションに名前が設定されていない場合がある為。 celPlaceで指定したセルか、objrefMarkerで指定したリファレンスの場所名を返す。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getPlaceName ; @function ; @global ; @param [celPlace=None] {Cell} ; @param [objrefMarker=None] {ObjectReference} ; @returns {String} ; ---------------------------------------------------------- String Function getPlaceName( \ Cell celPlace = None, \ ObjectReference objrefMarker = None \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Location locMarker; WorldSpace wsMarker; String szName = ""; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !celPlace && objrefMarker celPlace = objrefMarker.GetParentCell(); Elseif celPlace && !objrefMarker objrefMarker = celPlace.GetNthRef(0); Endif; !celPlace && objrefMarker ;------------------------------------------------------- ; Get Cell Name ;------------------------------------------------------- If celPlace szName = celPlace.GetName(); Endif; celPlace ;------------------------------------------------------- ; Get Location Name ;------------------------------------------------------- If (szName == "") If objrefMarker locMarker = objrefMarker.GetCurrentLocation(); EndIf; objrefMarker If locMarker szName = locMarker.GetName(); Endif; locMarker Endif; (szName == "") ;------------------------------------------------------- ; Get World Name ;------------------------------------------------------- If (szName == "") If objrefMarker wsMarker = objrefMarker.GetWorldSpace(); EndIf; objrefMarker If wsMarker szName = wsMarker.GetName(); Endif; wsMarker Endif; (szName == "") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return szName; EndFunction; getPlaceName() }} *espファイルのゴミを消す 一度設定してしまったプロパティはespに保存されます。 その後、不要になった場合にそのまま消してしまうとPapyrus.logに以下のようなwarningメッセージを吐くespが出来上がります。 [12/24/2020 - 00:00:01PM] warning: Property <プロパティ名> on script <スクリプト名> attached to <オブジェクトのEDID> (オブジェクトのformID) cannot be initialized because the script no longer contains that property espが自身に保存されたプロパティ情報を元に初期化しようとしたがスクリプトには存在しないために出る注意文で、新規ゲームでも関係なくメッセージを出します。 実害は無いですが邪魔な存在である事は確かなのでなるべくなら消しましょう。 -消し方 #ref(clear_value.jpg) 消したいプロパティを選択後clear valueを押し<<defalult>>にする事でespから情報を削除出来ます。 設定後、CKを保存してespを更新するのを忘れないでください。 更新を忘れて、スクリプトからプロパティを削除してしまうと注意文は消えません。 消してしまった場合、一度スクリプトにプロパティを加えてコンパイルし直してください。 なお、これはセーブデータに残ってしまった情報とは違います。 こういうの↓ 12/24/2020 - 01:00:00PM] warning: Could not find type <オブジェクトのEDIDやAlias> in the type table in save これはセーブデータに残ってしまった情報で確実な方法は発見されていません(2013/07/04現在) セーブデータに残ったスクリプト情報の残骸は[[Save game script cleaner>http://www.nexusmods.com/skyrim/mods/52363/]]というツールで消せます。 *扉の開錠/施錠 -サンプル: &ref(ExampleUnlockDoor.zip)(動かし方は中にある readme.txt を参照のこと) 扉の施錠判断には [[ObjectReference.IsLocked>http://www.creationkit.com/index.php?title=IsLocked_-_ObjectReference]] を使います。扉の開錠/施錠には [[ObjectReference.Lock>http://www.creationkit.com/index.php?title=Lock_-_ObjectReference]] を使います。 以下は、アクティベートイベントを元に、対象の扉が開いている場合は施錠/閉じている場合は解錠するスクリプトの例です。 Scriptname ExampleUnlockDoor extends ObjectReference ObjectReference Property targetDoor Auto Event OnActivate(ObjectReference akActionRef) if (targetDoor.IsLocked()) Debug.Notification("Unlock") targetDoor.Lock(false) else Debug.Notification("Lock") targetDoor.Lock(true) endIf endEvent targetDoor プロパティに事前に対象となる扉を指定する必要があります。 また、Creation Kit にて事前に扉の状態を決めるには Lock タブにある情報を設定します。 以下、スクリーンショットでは鍵を必要とする状態で扉を閉めています。 #ref(set_lock.png) 何らかのイベント等をトリガーに先に進めるようにする場合などに使えます。 *オブジェクトの移動 -サンプル: &ref(ExampleMoveObject.zip)(動かし方は中にある readme.txt を参照のこと) 設置済みのオブジェクトを移動させるには [[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] を使います。 以下はアクティベートした対象そのものを後ろへ移動させるスクリプトの例です。 Scriptname ExampleMoveObject extends ObjectReference Event OnActivate(ObjectReference akActionRef) SetPosition(x, y - 128, z) endEvent [[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] で指定する座標は、セル内の絶対座標系の値です。 [[ObjectReference>http://www.creationkit.com/index.php?title=ObjectReference_Script]] には現在位置を表す x, y, z プロパティがあるので、それを使って相対位置を指定するのが常套手段です。 向きも変えたい場合は [[ObjectReference.SetAngle>http://www.creationkit.com/index.php?title=SetAngle_-_ObjectReference]] を併用します。 ''※ポイント'':この移動方法は、今の座標から徐々に指定の場所へ移動させるものではありません。[[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] は、瞬時の移動しかできません。徐々に移動させるには前提として対象が [[Actor>http://www.creationkit.com/index.php?title=Actor_Script]] である必要があります。 *新しいオブジェクトの設置 **簡単な例 -サンプル: &ref(ExampleSetObject.zip)(動かし方は中にある readme.txt を参照のこと) オブジェクトを新しく設置するには [[ObjectReference.PlaceAtMe>http://www.creationkit.com/index.php?title=PlaceAtMe_-_ObjectReference]] を使います。 第一引数に [[Form>http://www.creationkit.com/index.php?title=Form_Script]] オブジェクトを指定します。 オブジェクト指向熟練者への補足ですが、以下の対比がちょうど当てはまると覚えておくと良いでしょう。 |[[Form>http://www.creationkit.com/index.php?title=Form_Script]] オブジェクト|クラス| |[[ObjectReference.PlaceAtMe>http://www.creationkit.com/index.php?title=PlaceAtMe_-_ObjectReference]]|new 演算子| |[[ObjectReference>http://www.creationkit.com/index.php?title=ObjectReference_Script]] オブジェクト|インスタンス| 以下(ExampleSetObject サンプル)はアクティベートする度に WETempActivator が後ろに増えていくスクリプトの例です。 Scriptname ExampleSetObject extends ObjectReference Activator Property setObject Auto int count = 0 Event OnActivate(ObjectReference akActionRef) count = count + 1 ObjectReference newObject = PlaceAtMe(setObject, 1) newObject.SetPosition(x, y - count * 128, z) endEvent サンプルは WETempActivator を新しく設置するものですが、他のものでも Form オブジェクトがあれば新しいオブジェクトを生成できます。 **スキーヴァーを大量発生させる例 -サンプル: &ref(ExampleSetSkeever.zip)(動かし方は中にある readme.txt を参照のこと) アクティベートするたびにスキーヴァーが部屋のどこかに設置されるサンプルです。 Scriptname ExampleSetSkeever extends ObjectReference ActorBase Property skeever Auto Event OnActivate(ObjectReference akActionRef) float newx = -1000 + 2000 * Utility.RandomFloat() float newy = -1000 + 2000 * Utility.RandomFloat() Actor newActor = PlaceAtMe(skeever, 1) as Actor newActor.SetPosition(newx, newy, z) newActor.StopCombat() endEvent skeever プロパティには「EncSkeever」が設定してあります。何度もアクティベートすれば当然…。 #ref(skeevers.jpg) 囲まれます。 **SpawnerTaskを使用して大量発生させる SpawnerTaskを使えば、敵やオブジェクトを効率よくスポーンさせることが出来ます。 下記の例では、PCの周囲に一気に出現させています。 #highlight(linenumber,php){{ Bool Function spawnAroundPlayer(Form frmTarget, Int nAddNum = 1) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); Float[] aPos = new Float[3]; Float[] aRot = new Float[3]; Int nTaskID; ;------------------------------------------------------- ; Spawn ;------------------------------------------------------- If frmTarget && (nAddNum > 0) nTaskID = SpawnerTask.Create(); SpawnerTask.AddSpawn( \ nTaskID, frmTarget, actPlayer, aPos, aRot, nAddNum); SpawnerTask.Run(nTaskID); Endif; frmTarget && (nAddNum > 0) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; spawnAroundPlayer() }} **FormList を使ったランダム設置の例 -サンプル: &ref(ExampleSetRandomObject.zip)(動かし方は中にある readme.txt を参照のこと) アクティベートするたびに [[FormList>http://www.creationkit.com/index.php?title=FormList_Script]] の中にある何かが部屋のどこかに設置されるサンプルです。 FormList には ObjectWindow の「WorldObjects/Tree/Plants」カテゴリにある植物群が入っており、設置後、素材を採集できます。 Scriptname ExampleSetRandomObject extends ObjectReference FormList Property flowers Auto Event OnActivate(ObjectReference akActionRef) float newx = -1000 + 2000 * Utility.RandomFloat() float newy = -1000 + 2000 * Utility.RandomFloat() Debug.Notification("Activate ... new object (" + newx + ", " + newy + ")") ObjectReference newObject = PlaceAtMe(flowers.GetAt(Utility.RandomInt(0, flowers.GetSize() - 1)), 1) newObject.SetPosition(newx, newy, z) endEvent *アクターの移動 **アクターの誘導 -サンプル: &ref(ExampleSkeeverAroundStone.zip)(動かし方は中にある readme.txt を参照のこと) アクターとは動きまわるオブジェクトのことです。プレイヤーや敵やNPCなどが該当します。 「オブジェクトの移動」で紹介した [[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] は瞬時の移動です。誘導には [[Actor.PathToReference>http://www.creationkit.com/index.php?title=PathToReference_-_Actor]] を使います。 誘導する際、移動先のオブジェクトを指定する必要があります。[[ObjectReference>http://www.creationkit.com/index.php?title=ObjectReference_Script]] であれば何でも良いですが、もしも自由に誘導させたいならば XMarker を使うのがお勧めです。 Scriptname ExampleSkeeverAroundStone extends ObjectReference Actor Property skeever Auto ObjectReference Property destination Auto int angle = 0 Event OnCellLoad() RegisterForSingleUpdate(1) endEvent Event OnUpdate() float distance = skeever.GetDistance(destination) if (distance < 64) angle = angle + 45 destination.SetPosition(x + 256 * Math.cos(angle), y + 256 * Math.sin(angle), z) endIf skeever.PathToReference(destination, 0.5) RegisterForSingleUpdate(1) endEvent 以上のサンプルはスキーヴァーが石碑の周りをぐるぐると回らせるものです。 destination に XMarker をセットし、スキーヴァーの到着を判断して位置を変えていきます。 また、本サンプルは「常時稼動させるスクリプト」のサンプルにもなっています。 **隊列の形成 KeepOffsetFromActor()を使えば、対象のアクターを別のアクターの隣や背後といった定位置に移動させることが出来る。 隊列の形成などは、この手法が使われていることが多い。 #highlight(linenumber,php){{ Actor actTarget; Actor actFollow; actTarget.KeepOffsetFromActor( \ actFollow, \ 170.0, -170.0, 0.0, \ afCatchUpRadius = 260.0, \ afFollowRadius = 8.0 ); }} *倒した敵からコンテナードロップ -サンプル: &ref(ExampleDropChest.zip)(動かし方は中にある readme.txt を参照のこと) 敵を倒した際、コンテナーをドロップさせてアイテムをゲットできるようにしてみましょう。 方法は簡単です。[[Actor.OnDeath>http://www.creationkit.com/index.php?title=OnDeath_-_Actor]] イベントで敵が倒されたタイミングを見計らい -コンテナーを設置 -敵を排除 するだけです。以下、事例です。 Scriptname ExampleDropChest extends ObjectReference Container Property BaseChest Auto Event OnDeath(Actor akKiller) ObjectReference chest = PlaceAtMe(BaseChest, 1) Disable(false) Delete() chest.SetAngle(0, 0, chest.z) EndEvent BaseChest に事前にドロップさせるコンテナーをセットしておく必要があります。 コンテナーを設置したら、敵本体を即時に無効にして削除します。 サンプルではスキーヴァー(EncSkeever)がフィールドにいます。倒すと空の宝箱(TreasDraugrChestEMPTYSmall)がドロップします。 最後にアングルを変えているのは、敵が死亡時、横たわるのでそれに合わせて宝箱も傾いてしまうのを補正するためです。 *キルムーブ時のイベントの取得 要:SKSE Event Init() RegisterForCameraState();まず別のイベントで登録する EndEvent Event OnPlayerCameraState() if newState == 2 ;キルムーブのカメラ切り替え EndIf EndEvent *NPCのインベントリから見えない装備を外す方法 単に今着ている装備を外すだけだと、ロードを挟んだ際に元々着ていた服に戻ります。 これを防ぐためには透明な装備を登録したOutfitをSetすればよいです。 価値を0にしておけば別の装備を渡したときにそちらを優先してきてくれます。 なおOutfitをSetすると元の服装に戻らなくなるため、予め回収しておくのもよいでしょう。 Outfit Property NakedOutfit Auto ; AAの無い装備を一つ登録したOutfit Function ExampleFunction(Actor akNPC) ; 単に今着ている装備を外したい場合 akNPC.UnequipAll() akNPC.RemoveAllItems() ; アイテムを回収したい場合は自分を指定 ; akNPC.RemoveAllItems(Game.GetPlayer()) ; 恒久的に装備を外してしまいたい場合 akNPC.SetOutfit(NakedOutfit) EndFunction *ラグドール状態の取得 ラグドールはフスロダなどで吹っ飛んでいるとき、または死亡時の手足がブラブラした状態のことです。 判定方法は二通りありますが片方はアクターが『フロスダやマヒ等で倒れているか』を判定する時に使い、 もう片方はオブジェクトが重力の影響下や固定されてるオブジェであるかの判定に使う事を推奨します。(理由は後述) **powerofthree's Papyrus ExtenderのGetActorKnockState関数を使う こちらは対象がアクターでより正確にラグドール状態を取得したい場合に有効となります。 GetActorKnockState関数はアクターがフロスダやマヒ(あるいはPushActorAway)等で倒れてラグドール状態になり、起き上がりが終わるまでは0以外を返します。 ただし、あくまで死亡以外の要因でラグドール状態になった時に判定するためのもので、死亡が原因でラグドールになった場合はGetActorKnockStateは0を返すためIsDeadによるチェックも必要です。 (マヒ状態や吹っ飛んでる途中で死亡した場合はGetActorKnockStateは0以外を返します) #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isInRagdollActor ; @desc 対象の人物がフロスダ等によるラグドール影響下にあるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @param isDeadRagdollCheck {Bool} 死亡からラグドール状態に移行した場合を考慮するか ; @returns {Bool} 影響下にあればtrue ; ---------------------------------------------------------- Bool Function isInRagdollActor(Actor actTarget, Bool isDeadRagdollCheck = true) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isInRagdoll = false; ;------------------------------------------------------- ; Checking Ragdoll Status ;------------------------------------------------------- If actTarget ; 死亡時にラグドール状態に移行する場合はGetActorKnockStateは0を返すため ; 従来のGetMass関数による判定と同じ挙動を行いたい時はIsDeadのチェックを加える If (PO3_SKSEFunctions.GetActorKnockState(actTarget) != 0) isInRagdoll = true ElseIf isDeadRagdollCheck isInRagdoll = actTarget.IsDead() EndIf Endif; actTarget ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isInRagdoll; EndFunction; isInRagdollActor() }} **GetMass関数を使う(従来のやり方、SE版ではアクターに対しては非推奨) アクターが吹っ飛んでいるかの判定では最もメジャーな方法でしたが、[[Precision>https://www.nexusmods.com/skyrimspecialedition/mods/72347]]の登場により事情が変わりました。 このMod環境下では武器攻撃の判定をより正確な判定にするのにHavok衝突判定を使うため、アクターに対してアクティブラグドールというものを付与されるようになります。 その影響でこのModの環境下の場合はアクターがラグドール状態でなくてもGetMassが0以外を返すようになります。 そのため、Precision環境下でアクターがふっ飛ばされてラグドールになってるかをGetMass関数で判定すると問題が発生する可能性があるため注意が必要です。 Precision環境でない場合はアクターに対するGetMass関数は通常時は常に0を返しますが、ラグドール中だけは設定されているラグドールの計算に使う重量(Mass)を返します。 ※フロスダなどで吹っ飛んでから静止すると起き上がりモーションが発生しますが、このモーション中もラグドール扱いとして0以外を返します。 Precision環境下でなくても今後出てくるModによってはGetMass関数がアクターに対して通常時でも0以外を返すようになる可能性があるため前述のGetActorKnockState関数でラグドール判定を行う事を推奨します。 例: if Game.GetPlayer().GetMass() == 0 ;ラグドール中じゃないとき debug.SendAnimationEvent(Game.GetPlayer(),"staggerStart") endif #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isInRagdoll ; @desc 対象の人や物がラグドール影響下にあるかを返す ; @function ; @global ; @param obrfThis {ObjectReference} 対象の人や物 ; @returns {Bool} 影響下にあればtrue ; ---------------------------------------------------------- Bool Function isInRagdoll(ObjectReference obrfThis) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isInRagdoll = false; ;------------------------------------------------------- ; Checking Ragdoll Status ;------------------------------------------------------- If obrfThis && (obrfThis.GetMass() != 0) isInRagdoll = true; Endif; obrfThis && (obrfThis.GetMass() != 0) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isInRagdoll; EndFunction; isInRagdoll() }} **ラグドール中のモーション再生によるバグ ラグドール中あるいは起き上がってる途中に別のモーションを再生すると、その場で固定されて行動不能になるバグがあります。ラグドール中は基本的に別のモーションでは割り込めないですが、ドラウグルはよろめき(StaggerStart)が割り込めてしまうので、上記の条件で除外しましょう。 *十進数と十六進数の変換 Papyrusには、こういった標準的な関数がないので不便する場合がある。 **十進数→十六進数 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name convDecToHex ; @desc 整数値を十六進数表記の文字列に変換 ; @function ; @global ; @param nNum {Int} 変換したい整数値 ; @returns {String} 十六進数表記の文字列 ; ---------------------------------------------------------- String Function convDecToHex(Int nNum) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- String szHex; Bool isNegative; Int[] aNums = new Int[8]; String[] aHex = new String[8]; Int nIdx = 0; Int nLen = aNums.Length; ;------------------------------------------------------- ; Checking over 0x80000000 ;------------------------------------------------------- If (nNum < 0) isNegative = True; nNum += 0x80000000; Endif ;------------------------------------------------------- ; Parse integer ;------------------------------------------------------- aNums[0] = (nNum / 0x10000000); nNum -= (aNums[0] * 0x10000000); aNums[1] = (nNum / 0x01000000); nNum -= (aNums[1] * 0x01000000); aNums[2] = (nNum / 0x00100000); nNum -= (aNums[2] * 0x00100000); aNums[3] = (nNum / 0x00010000); nNum -= (aNums[3] * 0x00010000); aNums[4] = (nNum / 0x00001000); nNum -= (aNums[4] * 0x00001000); aNums[5] = (nNum / 0x00000100); nNum -= (aNums[5] * 0x00000100); aNums[6] = (nNum / 0x00000010); nNum -= (aNums[6] * 0x00000010); aNums[7] = nNum; ;------------------------------------------------------- ; Convert to HEX ;------------------------------------------------------- If isNegative aNums[0] = aNums[0] + 8; Endif While (nIdx < nLen) If (aNums[nIdx] == 10) aHex[nIdx] = "A"; Elseif (aNums[nIdx] == 11) aHex[nIdx] = "B"; Elseif (aNums[nIdx] == 12) aHex[nIdx] = "C"; Elseif (aNums[nIdx] == 13) aHex[nIdx] = "D"; Elseif (aNums[nIdx] == 14) aHex[nIdx] = "E"; Elseif (aNums[nIdx] == 15) aHex[nIdx] = "F"; Else aHex[nIdx] = (aNums[nIdx] as String); Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Join characters ;------------------------------------------------------- szHex = aHex[0] + aHex[1] + aHex[2] + aHex[3] \ + aHex[4] + aHex[5] + aHex[6] + aHex[7]; ;------------------------------------------------------- ; Return ;------------------------------------------------------- return szHex; EndFunction; convDecToHex() }} **十六進数→十進数 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name convHexToDec ; @desc 十六進数表記の文字列を整数値で応答 ; @function ; @global ; @param szHex {String} 十六進数表記の文字列 ; @returns {Int} 変換した整数値 ; ---------------------------------------------------------- Int Function convHexToDec(String szHex) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nIdx = 0; Int nLen = StringUtil.GetLength(szHex); String cChar; Int nNum; Int nRet; ;------------------------------------------------------- ; Parse Hex-String ;------------------------------------------------------- While (nIdx < nLen) cChar = StringUtil.GetNthChar(szHex, nIdx); If StringUtil.IsDigit(cChar) nNum = cChar as Int; Elseif (cChar == "A") || (cChar == "a") nNum = 10; Elseif (cChar == "B") || (cChar == "b") nNum = 11; Elseif (cChar == "C") || (cChar == "c") nNum = 12; Elseif (cChar == "D") || (cChar == "d") nNum = 13; Elseif (cChar == "E") || (cChar == "e") nNum = 14; Elseif (cChar == "F") || (cChar == "f") nNum = 15; Else nNum= 0; Endif nNum = (nNum * Math.pow(16, (nLen - (nIdx + 1)))) as Int; nRet += nNum; nIdx += 1 EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nRet; EndFunction; convHexToDec() }} *攻撃側と防御側の位置と角度判定 バックスタブなどのMODで使われる手法。二者の相対的な距離と角度から判定している。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getAttackAngle ; @desc 攻撃側と防御側の向きと距離のタイプを返す ; @function ; @global ; @param actAtk {Actor} 攻撃側の人 ; @param actDef {Actor} 防御側の人 ; @param [nCloseRange=170.0] {Float} 近接と判断する距離限界 ; @param [nFrontAngle=44.0] {Float} 正面と判断する角度限界 ; @param [nRearAngle=125.0] {Float} 背面と判断する角度限界 ; @returns {Int} ; 距離のタイプとして、以下を返す ; -1 : 以下のどれでもない、または処理エラー ; 11 : 正面近距離 ; 12 : 背面近距離 ; 21 : 正面遠距離 ; 22 : 背面遠距離 ; ---------------------------------------------------------- Int Function getAttackAngle( \ Actor actAtk, \ Actor actDef, \ Float nCloseRange = 170.0, \ Float nFrontAngle = 44.0, \ Float nRearAngle = 125.0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nRet = -1; Float nFaceMin; Float nFaceMax; Float nRearMin; Float nRearMax; Float nDist; Float nAngleAtk; Float nAngleDef; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !actAtk || !actDef || (actAtk == actDef) return nRet; Endif; !actAtk || !actDef || (actAtk == actDef) If (nCloseRange < 0.0) nCloseRange *= -1.0; Endif; (nCloseRange < 0.0) If (nFrontAngle < 0.0) nFrontAngle *= -1.0; Endif; (nFrontAngle < 0.0) If (nFrontAngle > 90.0) nFrontAngle = 90.0; Endif; (nFrontAngle > 90.0) If (nRearAngle < 0.0) nRearAngle *= -1.0; Endif; (nRearAngle < 0.0) If (nRearAngle < 90.0) nRearAngle = 90.0; Elseif (nRearAngle > 180.0) nRearAngle = 180.0; Endif; (nRearAngle < 90.0) ;------------------------------------------------------- ; Init Range Values ;------------------------------------------------------- nFaceMin = nFrontAngle * -1.0; nFaceMax = nFrontAngle; nRearMin = nRearAngle * -1.0; nRearMax = nRearAngle; ;------------------------------------------------------- ; Get Distance and Angles ;------------------------------------------------------- nDist = actAtk.GetDistance(actDef); nAngleAtk = actAtk.GetHeadingAngle(actDef); nAngleDef = actDef.GetHeadingAngle(actAtk); ;------------------------------------------------------- ; Check Distance ;------------------------------------------------------- If (nDist <= nCloseRange) nRet = 10; Else nRet = 20; Endif; (nDist <= nCloseRange) ;------------------------------------------------------- ; Check Defender Angle ;------------------------------------------------------- If (nAngleAtk >= nFaceMin) && (nAngleAtk <= nFaceMax) If (nAngleDef >= nFaceMin) \ && (nAngleDef <= nFaceMax) nRet += 1; Elseif (nAngleDef <= nRearMin) \ || (nAngleDef >= nRearMax) nRet += 2; Else nRet = -1; Endif; (nDist <= nCloseRange) && ... Else nRet = -1; Endif; (nAngleAtk >= nFaceMin) && ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nRet; EndFunction; getAttackAngle() }} *コンソールやクロスヘアで選択した対象を取得 コンソールやクロスヘアで選択中のオブジェクトのリファレンスを取得したい場合の処理。 Game.GetCurrentConsoleRef()は昔のSKSEではサポートしていなかった関数なので、念の為にバージョンをチェックしている。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getObjectSelected ; @desc 選択中のオブジェクトを取得する ; @function ; @global ; @param [enableCrosshair=True] {Bool} ; クロスヘア選択を有効にする ; @param [enableConsole=True] {Bool} ; コンソール選択を有効にする ; @returns {ObjectReference} ; 取得した収納オブジェクト ; ---------------------------------------------------------- ObjectReference Function getObjectSelected( \ Bool enableCrosshair = True, \ Bool enableConsole = True \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ObjectReference obrfFind; Int nSkseVer = SKSE.GetScriptVersionRelease(); ;------------------------------------------------------- ; Console selected ;------------------------------------------------------- If !obrfFind && enableConsole && (nSkseVer > 47) obrfFind = \ Game.GetCurrentConsoleRef() as ObjectReference; Endif ;------------------------------------------------------- ; Crosshair ;------------------------------------------------------- If !obrfFind && enableCrosshair obrfFind = \ Game.GetCurrentCrosshairRef() as ObjectReference; Endif ;------------------------------------------------------- ; Return ;------------------------------------------------------- Return obrfFind; EndFunction; getObjectSelected() }} *ゲーム内の時間取得 Utility.GetCurrentGameTime()でゲーム内の経過日数を取得してから計算する。 **ゲーム内時間の週を数値で返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getCurrentWeekIdx ; @desc ゲーム内時間の週を数値で返す ; @function ; @global ; @returns {Int} 週を示す整数値(0 - 6) ; ---------------------------------------------------------- Int Function getCurrentWeekIdx() global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Float nGameTime = Utility.GetCurrentGameTime(); Int nDays = Math.Floor(nGameTime); Int nWeeks = Math.Floor(nDays / 7); Int nWeekIdx = nDays - (nWeeks * 7); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nWeekIdx; EndFunction; getCurrentWeekIdx() }} **ゲーム内時間の時間を数値で返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getCurrentHour ; @desc ゲーム内時間の時間を数値で返す ; @function ; @global ; @returns {Int} 時間を示す整数値(0 - 23) ; ---------------------------------------------------------- Int Function getCurrentHour() global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Float nGameTime = Utility.GetCurrentGameTime(); Int nDays = Math.Floor(nGameTime); Int nHours = Math.Floor((nGameTime - nDays) * 24); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nHours; EndFunction; getCurrentHour() }} *判定処理 様々な判定を行う処理の例。 **対象の人が敵性であるかを返す この例では、敵と判定する為の3つの条件を組み合わせている。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isEnemy ; @desc 対象の人が敵性であるかを返す ; @function ; @global ; @param actEnemy {Actor} 対象の人 ; @param [actTarget=None] {Actor} ; 誰に対する敵かを指定(None時はプレイヤーに対する敵) ; @returns {Bool} 敵性であればtrue ; ---------------------------------------------------------- Bool function isEnemy(Actor actEnemy, Actor actTarget = None) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isEnemy = false; Int nReaction; Int nRank; ;------------------------------------------------------- ; Check Argument ;------------------------------------------------------- If !actEnemy return isEnemy; Elseif !actTarget actTarget = Game.GetPlayer(); Endif; !actEnemy ;------------------------------------------------------- ; Check Actor ;------------------------------------------------------- If (actEnemy == actTarget) return isEnemy; Endif; (actEnemy == actTarget) ;------------------------------------------------------- ; Check Hostile ;------------------------------------------------------- If !isEnemy isEnemy = actEnemy.IsHostileToActor(actTarget); Endif; !isEnemy ;------------------------------------------------------- ; Check Reaction ;------------------------------------------------------- If !isEnemy ; Obtains this actors faction-based reaction ; to the other actor ; 0 - Neutral ; 1 - Enemy ; 2 - Ally ; 3 - Friend nReaction = actEnemy.getFactionReaction(actTarget); isEnemy = (nReaction == 1); Endif; !isEnemy ;------------------------------------------------------- ; Check Relationship ;------------------------------------------------------- If !isEnemy ; Relationship functions use the following values: ; 4 - Lover ; 3 - Ally ; 2 - Confidant ; 1 - Friend ; 0 - Acquaintance ; -1 - Rival ; -2 - Foe ; -3 - Enemy ; -4 - Archnemesis nRank = actEnemy.GetRelationshipRank(actTarget); isEnemy = (nRank <= -2); Endif; !isEnemy ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isEnemy; EndFunction; isEnemy() }} **対象の人がフォロワーであるかを返す フォロワーMODを使用している場合では、判定条件が異なるかも知れない。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isFollower ; @desc 対象の人がフォロワーであるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} フォロワーであればtrue ; ---------------------------------------------------------- Bool Function isFollower(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Faction[] aFollower; Bool isFollower = false; ;------------------------------------------------------- ; Get Factions ;------------------------------------------------------- ; CurrentFollowerFaction 0x5C84E ; CurrentHireling 0xBD738 aFollower = new Faction[2]; aFollower[0] = Game.GetForm(0x5C84E) As Faction; aFollower[1] = Game.GetForm(0xBD738) As Faction; ;------------------------------------------------------- ; Check Factions ;------------------------------------------------------- If actTarget.IsInFaction(aFollower[0]) \ || actTarget.IsInFaction(aFollower[1]) isFollower = true; Endif; actTarget.IsInFaction(aFollower[0]) || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isFollower; EndFunction; isFollower() }} **対象の人が魔法使いであるかを返す この例では、戦闘スタイルで魔法使いかを判定している。 この判定条件が有効かどうかは、使う場面による。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isMagicUser ; @desc 対象の人が魔法使いであるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} 魔法使いであればtrue ; ---------------------------------------------------------- Bool Function isMagicUser(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); CombatStyle csTarget = abTarget.GetCombatStyle(); Float nMeleeMult = csTarget.GetMeleeMult(); Float nMagicMult = csTarget.GetMagicMult(); Float nRangedMult = csTarget.GetRangedMult(); Bool isMagicUser = false; ;------------------------------------------------------- ; Check ;------------------------------------------------------- If (nMagicMult > nMeleeMult) || (nMagicMult > nRangedMult) isMagicUser = true; Endif; (nMagicMult > nMeleeMult) || (nMagicMult > nRangedMult) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isMagicUser; EndFunction; isMagicUser() }} **対象の人がクリーチャーであるかを返す この例では、人間タイプでないことをクリーチャーの条件としている。 MODで、ActorTypeCreatureのキーワードがないクリーチャーが多い為。 良し悪しあるので、ケェス・バイ・ケェスではある。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isCreature ; @desc 対象の人がクリーチャーであるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} クリーチャーであればtrue ; ---------------------------------------------------------- Bool Function isCreature(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Int nSex = abTarget.GetSex(); Bool isCreature = false; ;------------------------------------------------------- ; Check Keyword ;------------------------------------------------------- If !actTarget.HasKeywordString("ActorTypeNPC") isCreature = true; Elseif (nSex == -1) isCreature = true; Endif; !actTarget.HasKeywordString("ActorTypeNPC") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isCreature; EndFunction; isCreature() }} **対象の人が人間男性であるかを返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isMale ; @desc 対象の人が人間男性であるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} 人間男性であればtrue ; ---------------------------------------------------------- Bool Function isMale(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Int nSex = abTarget.GetSex(); Bool isMale = false; ;------------------------------------------------------- ; Check Keyword ;------------------------------------------------------- If !actTarget.HasKeywordString("ActorTypeNPC") isMale = false; Elseif (nSex == 0) isMale = true; Endif; !actTarget.HasKeywordString("ActorTypeNPC") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isMale; EndFunction; isMale() }} **対象の人が人間女性であるかを返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isFemale ; @desc 対象の人が人間女性であるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} 人間女性であればtrue ; ---------------------------------------------------------- Bool Function isFemale(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Int nSex = abTarget.GetSex(); Bool isFemale = false; ;------------------------------------------------------- ; Check Keyword ;------------------------------------------------------- If !actTarget.HasKeywordString("ActorTypeNPC") isFemale = false; Elseif (nSex == 1) isFemale = true; Endif; !actTarget.HasKeywordString("ActorTypeNPC") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isFemale; EndFunction; isFemale() }} **対象の人が戦闘継続可能であるかを返す その瞬間に継続可能かどうかの判定。GetMass()はラグドール状態かの判定。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name canCombat ; @desc 対象の人が戦闘継続可能であるかを返す ; @function ; @global ; @param [actTarget=None] {Actor} ; 対象の人 (None時はプレイヤーが対象) ; @returns {Bool} ; 戦闘継続可能であればtrue ; ---------------------------------------------------------- Bool Function canCombat(Actor actTarget = None) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool canCombat = true; ;------------------------------------------------------- ; Checking Actor ;------------------------------------------------------- If !actTarget actTarget = Game.GetPlayer(); Endif; !actTarget ;------------------------------------------------------- ; Checking Keywords ;------------------------------------------------------- If !actTarget.Is3DLoaded() \ || actTarget.IsDisabled() \ || actTarget.IsDead() \ || actTarget.IsUnconscious() \ || actTarget.IsBleedingOut() \ || (actTarget.GetMass() != 0) canCombat = false; Endif; !actTarget.Is3DLoaded() || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return canCombat; EndFunction; canCombat() }} **パワーアタック判定 スプリント攻撃以外はアニメーション変数「bAllowRotation」で判定できる。 #highlight(linenumber,php){{ ; Animation event, sent when an object we are listening ; to hits one of the events we are listening for Event OnAnimationEvent(ObjectReference akSource, string asEventName) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Actor actTarget = akSource as Actor; Bool isPowerAtk = true; ;------------------------------------------------------- ; Check Power Attack ;------------------------------------------------------- If (asEventName == "weaponSwing") \ || (asEventName == "weaponLeftSwing") isPowerAtk = \ actTarget.GetAnimationVariableBool( \ "bAllowRotation" ); Endif; (szEvent == "weaponSwing") || ... EndEvent; OnAnimationEvent() }} ***The Ultimate Dodge Mod か Nemesis使用環境の場合 The Ultimate Dodge Modを使用している場合か、 アニメーション管理ツールのNemesis実行時に「The Ultimate Dodge Mod」にチェックを入れて処理した場合、人間NPCのアニメーションから受信できるAnimationEventが増える。 通常攻撃時は"NextAttackInitiate"、パワーアタック時は"NextPowerAttackInitiate"が発生する。 (スプリントのパワーアタック時も"NextPowerAttackInitiate"が発生する) #highlight(linenumber,php){{ Event OnAnimationEvent(ObjectReference akSource, string asEventName) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isPowerAtk = false; ;------------------------------------------------------- ; Check Power Attack ;------------------------------------------------------- If (asEventName == "NextAttackInitiate") isPowerAtk = false ElseIf (asEventName == "NextPowerAttackInitiate") isPowerAtk = true Endif; (asEventName == "NextAttackInitiate") ElseIf ... EndEvent; OnAnimationEvent() }} **設備や器具の判定 設備や器具であるかを判定する為の処理例。 基本的には、キーワードで判定することが可能。 椅子などは、キーワードで判定できない場合もあるので注意。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isSittingObject ; @desc 対象のオブジェクトが座るタイプの家具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 座るタイプの家具の場合はtrue ; ---------------------------------------------------------- Bool Function isSittingObject(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; IsTable [KYWD:0009B9A9] ; isBarStool [KYWD:00074EC7] If frmTarget.HasKeywordString("IsTable") \ || frmTarget.HasKeywordString("isBarStool") isObject = true; Endif; frmTarget.HasKeywordString("IsTable") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSittingObject() ; ---------------------------------------------------------- ; @name isSleepingObject ; @desc 対象のオブジェクトが寝るタイプの家具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 寝るタイプの家具の場合はtrue ; ---------------------------------------------------------- Bool Function isSleepingObject(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; FurnitureBedRoll [KYWD:000E4AD6] If frmTarget.HasKeywordString("FurnitureBedRoll") isObject = true; Endif; frmTarget.HasKeywordString("FurnitureBedRoll") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSleepingObject() ; ---------------------------------------------------------- ; @name isAlchemyLab ; @desc 対象のオブジェクトが錬金器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 錬金器具の場合はtrue ; ---------------------------------------------------------- Bool Function isAlchemyLab(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isAlchemy [KYWD:0002A40B] ; WICraftingAlchemy [KYWD:0004F6E6] If frmTarget.HasKeywordString("isAlchemy") \ || frmTarget.HasKeywordString("WICraftingAlchemy") isObject = true; Endif; frmTarget.HasKeywordString("isAlchemy") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isAlchemyLab() ; ---------------------------------------------------------- ; @name isCookingObject ; @desc 対象のオブジェクトが調理器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 調理器具の場合はtrue ; ---------------------------------------------------------- Bool Function isCookingObject(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isCookingSpit [KYWD:00068ADA] ; CraftingCookpot [KYWD:000A5CB3] ; isSmallCookingPot [KYWD:001010B2] ; isSmallCookingPotDBPoison [KYWD:001010B5] ; isCraftingOven [KYWD:01002840] If frmTarget.HasKeywordString("isCookingSpit") \ || frmTarget.HasKeywordString("isCraftingOven") \ || frmTarget.HasKeywordString("CraftingCookpot") \ || frmTarget.HasKeywordString("isSmallCookingPot") \ || frmTarget.HasKeywordString("isSmallCookingPotDBPoison") isObject = true; Endif; frmTarget.HasKeywordString("isCookingSpit") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isCookingObject() ; ---------------------------------------------------------- ; @name isEnchanter ; @desc 対象のオブジェクトが通常の付呪器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 通常の付呪器具の場合はtrue ; ---------------------------------------------------------- Bool Function isEnchanter(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isEnchanting [KYWD:0006E2A3] ; WICraftingEnchanting [KYWD:0004F6DD] ; DLC2StaffEnchanter [KYWD:02017738] If frmTarget.HasKeywordString("DLC2StaffEnchanter") isObject = false; Elseif frmTarget.HasKeywordString("isEnchanting") \ || frmTarget.HasKeywordString("WICraftingEnchanting") isObject = true; Endif; frmTarget.HasKeywordString("DLC2StaffEnchanter") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isEnchanter() ; ---------------------------------------------------------- ; @name isStaffEnchanter ; @desc 対象のオブジェクトが杖の付呪器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 杖の付呪器具の場合はtrue ; ---------------------------------------------------------- Bool Function isStaffEnchanter(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; DLC2StaffEnchanter [KYWD:02017738] If frmTarget.HasKeywordString("DLC2StaffEnchanter") isObject = true; Endif; frmTarget.HasKeywordString("DLC2StaffEnchanter") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isStaffEnchanter() ; ---------------------------------------------------------- ; @name isSharpeningWheel ; @desc 対象のオブジェクトが研ぎ石かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 研ぎ石の場合はtrue ; ---------------------------------------------------------- Bool Function isSharpeningWheel(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; WICraftingSmithingTempering [KYWD:0004F6FD] ; CraftingSmithingSharpeningWheel [KYWD:00088108] If frmTarget.HasKeywordString("WICraftingSmithingTempering") \ || frmTarget.HasKeywordString("CraftingSmithingSharpeningWheel") isObject = true; Endif; frmTarget.HasKeywordString("WICraftingSmithingTempering") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSharpeningWheel() ; ---------------------------------------------------------- ; @name isArmorTable ; @desc 対象のオブジェクトが作業机かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 作業机の場合はtrue ; ---------------------------------------------------------- Bool Function isArmorTable(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isBlacksmithWorkbench [KYWD:000D932E] ; CraftingSmithingArmorTable [KYWD:000ADB78] If frmTarget.HasKeywordString("isBlacksmithWorkbench") \ || frmTarget.HasKeywordString("CraftingSmithingArmorTable") isObject = true; Endif; frmTarget.HasKeywordString("isBlacksmithWorkbench") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isArmorTable() ; ---------------------------------------------------------- ; @name isForge ; @desc 対象のオブジェクトが鍛造器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 鍛造器具の場合はtrue ; ---------------------------------------------------------- Bool Function isForge(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isBlacksmithForge [KYWD:000CAE0A] ; CraftingSmithingForge [KYWD:00088105] ; isBlacksmithAnvil [KYWD:000EB60B] If frmTarget.HasKeywordString("isBlacksmithForge") \ || frmTarget.HasKeywordString("isBlacksmithAnvil") \ || frmTarget.HasKeywordString("CraftingSmithingForge") isObject = true; Endif; frmTarget.HasKeywordString("isBlacksmithForge") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isForge() ; ---------------------------------------------------------- ; @name isTanningRack ; @desc 対象のオブジェクトが皮なめしかどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 皮なめしの場合はtrue ; ---------------------------------------------------------- Bool Function isTanningRack(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isTanning [KYWD:000727A0] ; CraftingTanningRack [KYWD:0007866A] If frmTarget.HasKeywordString("isTanning") \ || frmTarget.HasKeywordString("CraftingTanningRack") isObject = true; Endif; frmTarget.HasKeywordString("isTanning") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isTanningRack() ; ---------------------------------------------------------- ; @name isSmelter ; @desc 対象のオブジェクトが溶鉱炉かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 溶鉱炉の場合はtrue ; ---------------------------------------------------------- Bool Function isSmelter(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isSmelter [KYWD:0009C6C3] ; CraftingSmelter [KYWD:000A5CCE] If frmTarget.HasKeywordString("isSmelter") \ || frmTarget.HasKeywordString("CraftingSmelter") isObject = true; Endif; frmTarget.HasKeywordString("isSmelter") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSmelter() ; ---------------------------------------------------------- ; @name isWoodChoppingBlock ; @desc 対象のオブジェクトが薪割りかどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 薪割りの場合はtrue ; ---------------------------------------------------------- Bool Function isWoodChoppingBlock(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; FurnitureWoodChoppingBlock / 0x00072dfb (KYWD) If frmTarget.HasKeywordString("FurnitureWoodChoppingBlock") isObject = true; Endif; frmTarget.HasKeywordString("FurnitureWoodChoppingBlock") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isWoodChoppingBlock() ; ---------------------------------------------------------- ; @name isOreVein ; @desc 対象のオブジェクトが鉱脈かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 鉱脈の場合はtrue ; ---------------------------------------------------------- Bool Function isOreVein(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isPickaxeFloor [KYWD:000613A8] ; isPickaxeTable [KYWD:000613A9] ; isPickaxeWall [KYWD:000A82C3] If frmTarget.HasKeywordString("isPickaxeFloor") \ || frmTarget.HasKeywordString("isPickaxeTable") \ || frmTarget.HasKeywordString("isPickaxeWall") isObject = true; Endif; frmTarget.HasKeywordString("isPickaxeFloor") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isOreVein() }} **盗み判定 盗みかどうかを判定する関数はない為、不法侵入と所有権で判定している。 #highlight(linenumber,php){{ Bool Function isStealing(ObjectReference obrfTarget, Actor actTarget) ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Bool isStealing; ActorBase abTarget; Cell celThis; ActorBase abOwnThis; Faction fctOwnThis; ActorBase abOwnHere; Faction fctOwnHere; ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- isStealing = actTarget.IsTrespassing(); abTarget = actTarget.GetActorBase(); celThis = obrfTarget.GetParentCell(); abOwnThis = obrfTarget.GetActorOwner(); fctOwnThis = obrfTarget.GetFactionOwner(); abOwnHere = celThis.GetActorOwner(); fctOwnHere = celThis.GetFactionOwner(); ;------------------------------------------------------- ; Check Stealing ;------------------------------------------------------- If abOwnThis && (abOwnThis != abTarget) isStealing = true; Elseif abOwnHere && (abOwnHere != abTarget) isStealing = true; Elseif fctOwnThis && !actTarget.IsInFaction(fctOwnThis) isStealing = true; Elseif fctOwnHere && !actTarget.IsInFaction(fctOwnHere) isStealing = true; Endif; abOwnThis && (abOwnThis != abTarget) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; isStealing() }} *配置オブジェクトの操作 セル内に配置されたオブジェクトを操作する処理。 **配置オブジェクトを一括で初期位置に戻す MoveToMyEditorLocation()が初期位置に戻す処理。 この例では、nObjectTypeでオブジェクトの種類を指定して、セル内のオブジェクトを一括で元の位置に戻している。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name resetPositionsByObjectTypeIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [nObjectType=0] {Int} ; @returns {Int} ; ---------------------------------------------------------- Int Function resetPositionsByObjectTypeIn( \ Cell celTarget = None, \ Int nObjectType = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ObjectReference obrfEach; Int nIdx; Int nCnt; Int nReset = 0; ;------------------------------------------------------- ; Check the target cell ;------------------------------------------------------- If !celTarget celTarget = Game.GetPlayer().GetParentCell(); Endif; !celTarget ;------------------------------------------------------- ; Loop of objects ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nObjectType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nObjectType); If obrfEach obrfEach.MoveToMyEditorLocation(); nReset += 1; Endif; obrfEach nIdx += 1; EndWhile; (nIdx < nCnt) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nReset; EndFunction; resetPositionsByObjectTypeIn() }} **配置オブジェクトを一括で固定化する SetMotionType()でMotion_Keyframedを指定すると、オブジェクトが固定化されて動かなくなる。 つまり、体当たりしても動かないオブジェクトになる。 この例では、nObjectTypeでオブジェクトの種類を指定して、セル内のオブジェクトを一括で固定化したり、固定化を解除したりしている。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name setMotionTypeByObjectTypeIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [enableLock=true] {Bool} ; @param [nObjectType=0] {Int} ; @returns {Int} ; ---------------------------------------------------------- Int Function setMotionTypeByObjectTypeIn( \ Cell celTarget = None, \ Bool enableLock = true, \ Int nObjectType = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ObjectReference obrfEach; Int nIdx; Int nCnt; Int nLockCnt = 0; ;------------------------------------------------------- ; Check the target cell ;------------------------------------------------------- If !celTarget celTarget = Game.GetPlayer().GetParentCell(); Endif ;------------------------------------------------------- ; Loop of objects ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nObjectType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nObjectType); ; Motion_Dynamic = 1 ; Motion_SphereIntertia = 2 ; Motion_BoxIntertia = 3 ; Motion_Keyframed = 4 ; Motion_Fixed = 5 ; Motion_ThinBoxIntertia = 6 ; Motion_Character = 7 If obrfEach.GetMass() && enableLock obrfEach.SetMotionType(obrfEach.Motion_Keyframed); nLockCnt += 1; Elseif !enableLock obrfEach.SetMotionType(obrfEach.Motion_Dynamic); nLockCnt += 1; Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nLockCnt; EndFunction; setMotionTypeByObjectTypeIn() }} **配置オブジェクトを一括で収集する 死体は単純にMoveTo()では移動できないので、Disable()してMoveTo()してEnable()している。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name gatherObjectsIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [obrfMoveTo=None] {ObjectReference} ; @param [nType=0] {Int} ; Form-Type ; @param [nIsDeadAlive=0] {Int} ; 0 = both, 1 = lives only, -1 = deads only ; @returns {Bool} ; ---------------------------------------------------------- Bool Function gatherObjectsIn( \ Cell celTarget = None, \ ObjectReference obrfMoveTo = None, \ Int nType = 0, \ Int nIsDeadAlive = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nIdx; Int nCnt; ObjectReference obrfEach; Actor actEach; ;------------------------------------------------------- ; Check arguments ;------------------------------------------------------- If !obrfMoveTo obrfMoveTo = Game.GetPlayer() as ObjectReference; Endif If !celTarget celTarget = obrfMoveTo.GetParentCell(); Endif ;------------------------------------------------------- ; Scan chests ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nType); If !obrfEach.IsEnabled() || obrfEach.IsDeleted() obrfEach = None; Endif If obrfEach && (nType == 43) actEach = obrfEach as Actor; Else actEach = None; Endif If actEach If (nIsDeadAlive > 0) && actEach.IsDead() obrfEach = None; actEach = None; Elseif (nIsDeadAlive < 0) && !actEach.IsDead() obrfEach = None; actEach = None; Endif Endif If actEach && (nType == 43) If actEach.IsDead() actEach.Disable(); Endif; actEach.IsDead() actEach.MoveTo(obrfMoveTo); If actEach.IsDead() actEach.Enable(); Endif; actEach.IsDead() Elseif !actEach && obrfEach obrfEach.MoveTo(obrfMoveTo); Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; gatherObjectsIn() }} **配置オブジェクトを一括で検知する 変性呪文の検知魔法のようなもの。スクリプトで実装すれば、好きなエフェクトを設定できる。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name detectObjectsIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [nType=0] {Int} ; Form-Type ; @param [isLootableOnly=false] {Bool} ; @param [nIsDeadAlive=0] {Int} ; 0 = both, 1 = lives only, -1 = deads only ; @returns {Bool} ; ---------------------------------------------------------- Bool Function detectObjectsIn( \ Cell celTarget = None, \ Int nType = 0, \ Bool isLootableOnly = false, \ Int nIsDeadAlive = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nIdx; Int nCnt; ObjectReference obrfEach; Actor actEach; Actor actPlayer = Game.GetPlayer(); EffectShader efsEnemy; EffectShader efsAlly; Hazard hzdDetect; Bool isLootable; ;------------------------------------------------------- ; Get Effect ;------------------------------------------------------- ; LifeDetected [EFSH:0x000146] ; LifeDetectedEnemy [EFSH:0x0DC209] ; LifeDetectedUndead [EFSH:0x0AAEB3] ; LifeDetectedUndeadEnemy [EFSH:0x016439] ; KynesPeaceFXS [EFSH:0x084B39] ; HealCircleFXS [EFSH:0x10CDC9] ; WerewolfTransFXS [EFSH:0x0EBEC5] ; WerewolfTrans02FXS [EFSH:0x0EBECD] ; DragonPowerAbsorbFXS [EFSH:0x0280C0] ; AbsorbBlueFXS [EFSH:0x0ABF08] ; AbsorbGreenFXS [EFSH:0x0ABF07] ; AbsorbHealthFXS [EFSH:0x0ABEFF] efsAlly = Game.GetForm(0x000146) as EffectShader; efsEnemy = Game.GetForm(0x0DC209) as EffectShader; ; CircleOfProtectionHazard [HAZD:0x04E80C] ; CircleVitalityHazard [HAZD:0x0B62EA] ; GuardianCircleHazard [HAZD:0x0E0CD3] ; GuardianCircleTurnHazard [HAZD:0x0FEAD3] hzdDetect = Game.GetForm(0x04E80C) as Hazard; ;------------------------------------------------------- ; Check the target cell ;------------------------------------------------------- If !celTarget celTarget = Game.GetPlayer().GetParentCell(); Endif ;------------------------------------------------------- ; Scan chests ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nType); If !obrfEach.IsEnabled() || obrfEach.IsDeleted() obrfEach = None; Endif If obrfEach && (nType == 43) actEach = obrfEach as Actor; Else actEach = None; Endif If actEach If (nIsDeadAlive > 0) && actEach.IsDead() obrfEach = None; actEach = None; Elseif (nIsDeadAlive < 0) && !actEach.IsDead() obrfEach = None; actEach = None; Endif Endif If !isLootableOnly isLootable = false; Elseif actEach isLootable = (actEach.GetNumItems() > 1); Elseif obrfEach && (nType == 28) isLootable = (obrfEach.GetNumItems() > 0); Else isLootable = false; Endif If isLootableOnly && !isLootable obrfEach = None; actEach = None; Endif If actEach && (nType == 43) If isEnemy(actEach) efsEnemy.Play(actEach, 26.0); Else efsAlly.Play(actEach, 26.0); Endif; isEnemy(actEach) Elseif !actEach && obrfEach obrfEach.placeAtMe(hzdDetect); Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; detectObjectsIn() }} *NPCを操作する NPCをPCのように操作する為に必要な一連の処理。 PCの操作停止、NPCの操作、カメラの切替、など。 #highlight(linenumber,php){{ Bool Function startNpcControl(Actor actTarget) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ;------------------------------------------------------- ; Start controlling ;------------------------------------------------------- Game.DisablePlayerControls( \ abMovement = false, \ abFighting = true, \ abCamSwitch = false, \ abLooking = false, \ abSneaking = false, \ abMenu = true, \ abActivate = false, \ abJournalTabs = false, \ aiDisablePOVType = 0 ); actTarget.SetPlayerControls(true); Game.SetPlayerAIDriven(true); actTarget.EnableAI(); Game.SetCameraTarget(actTarget); Game.ForceThirdPerson(); ;------------------------------------------------------- ; Return ;------------------------------------------------------- Return true; EndFunction; startNpcControl() Bool Function stopNpcControl(Actor actTarget) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Switch the controlling ;------------------------------------------------------- actTarget.SetPlayerControls(false); Game.SetPlayerAIDriven(false); Game.EnablePlayerControls(); actTarget.EnableAI(); Game.SetCameraTarget(actPlayer); ;------------------------------------------------------- ; Return ;------------------------------------------------------- Return true; EndFunction; stopNpcControl() }} *マップ・マーカーを表示する 指定したクエストのエイリアスに任意のリファレンスをマーカーとして表示する。 クエストは名前でも指定可能。エイリアスは名前やIDや番号でも指定可能。 例では、isActiveで表示と非表示を切り替える。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name showMapMarker ; @function ; @global ; @param obrfTarget {ObjectReference} ; @param [isActive=true] {Bool} ; @param [szQuestName=] {String} ; @param [nObjective=1] {Int} ; @param [szAliasName=RefAlias001] {String} ; @param [qstMarker=None] {Quest} ; @param [ralsMarker=None] {ReferenceAlias} ; @param [nAliasIdx=-1] {Int} ; @param [nAliasID=-1] {Int} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function showMapMarker( \ ObjectReference obrfTarget, \ Bool isActive = true, \ String szQuestName = "", \ Int nObjective = 1, \ String szAliasName = "RefAlias001", \ Quest qstMarker = None, \ ReferenceAlias ralsMarker = None, \ Int nAliasIdx = -1, \ Int nAliasID = -1 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isOK = false; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !obrfTarget return isOK; Endif ;------------------------------------------------------- ; Get Marker Quest ;------------------------------------------------------- If !qstMarker qstMarker = Quest.GetQuest(szQuestName); Endif If !qstMarker return isOK; Endif ;------------------------------------------------------- ; Get Reference Alias ;------------------------------------------------------- If !ralsMarker && (nAliasIdx >= 0) ralsMarker = \ qstMarker.GetNthAlias( \ nAliasIdx ) as ReferenceAlias; Elseif !ralsMarker && (nAliasID >= 0) ralsMarker = \ qstMarker.GetAliasById( \ nAliasID ) as ReferenceAlias; Elseif !ralsMarker && (szAliasName != "") ralsMarker = \ qstMarker.GetAliasByName( \ szAliasName ) as ReferenceAlias; Endif If !ralsMarker return isOK; Endif ;------------------------------------------------------- ; Show Marker ;------------------------------------------------------- If isActive ralsMarker.ForceRefTo(obrfTarget); qstMarker.SetObjectiveDisplayed( \ nObjective, true, false ); isOK = true; Else ralsMarker.Clear(); qstMarker.SetObjectiveDisplayed( \ nObjective, false, false ); isOK = true; Endif ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isOK; EndFunction; showMapMarker() }} *対象の人の戦闘を強制停止させる 戦闘を停止させる処理の詰め合わせ。 StopCombat()は対象のアクターの戦闘状態を停止させることしか出来ない。 本格的に戦闘停止させるには、警戒状態も解除し、相手方の戦闘状態も解除する必要がある。 disableRecoverのオプションは、戦闘停止時に全回復するようなMOD対策。 戦闘停止後に、停止前の体力に強制的に戻す為のオプション。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name stopCombatEx ; @function ; @global ; @param actTarget {Actor} ; @param [disableRecover=true] {Bool} ; @param [enableStopAlarm=false] {Bool} ; @param [enableStopTarget=false] {Bool} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function stopCombatEx( \ Actor actTarget, \ Bool disableRecover = true, \ Bool enableStopAlarm = false, \ Bool enableStopTarget = false \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Float nHealth; Float nDamage; Actor actEnemy; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !actTarget || !actTarget.IsInCombat() return false; Endif; !actTarget || !actTarget.IsInCombat() ;------------------------------------------------------- ; Get Current Health ;------------------------------------------------------- If disableRecover nHealth = actTarget.GetActorValue("Health"); Endif; disableRecover ;------------------------------------------------------- ; Stop Combat (Self) ;------------------------------------------------------- actTarget.StopCombat(); ;------------------------------------------------------- ; Stop Alarm ;------------------------------------------------------- If enableStopAlarm actTarget.StopCombatAlarm(); Endif; enableStopAlarm ;------------------------------------------------------- ; Stop Combat (Enemy) ;------------------------------------------------------- If enableStopTarget actEnemy = actTarget.GetCombatTarget(); If actEnemy stopCombatEx(actEnemy, disableRecover, false, false); Endif Endif; enableStopTarget ;------------------------------------------------------- ; Damage Health ;------------------------------------------------------- If disableRecover nDamage = actTarget.GetActorValue("Health") - nHealth; If (nDamage > 0.0) actTarget.DamageActorValue("Health", nDamage); Endif; (nDamage > 0.0) Endif; disableRecover ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; stopCombatEx() }} *外見変更 **顔テキスチャの変更 NetImmerse Overrideが必要。 アクターの顔テキスチャを変更する処理。顔用のTextureSetを指定する。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name changeFace ; @function ; @global ; @param actTarget {Actor} ; @param txtSkin {TextureSet} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function changeFace(Actor actTarget, TextureSet txtSkin) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget; Int nHeadIdx = -1; String szNode = ""; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !actTarget || !txtSkin return false; Else abTarget = actTarget.GetLeveledActorBase(); Endif; !actTarget || !txtSkin ;------------------------------------------------------- ; Get Head Index ;------------------------------------------------------- If abTarget nHeadIdx = abTarget.GetIndexOfHeadPartByType(1); Endif; abTarget ;------------------------------------------------------- ; Get Node Name ;------------------------------------------------------- If (nHeadIdx >= 0) szNode = abTarget.GetNthHeadPart(nHeadIdx).GetName(); Endif; (nHeadIdx >= 0) ;------------------------------------------------------- ; Apply Face Texture ;------------------------------------------------------- If (nHeadIdx >= 0) abTarget.SetFaceTextureSet(txtSkin); NetImmerse.SetNodeTextureSet( \ actTarget, szNode, txtSkin, false); Endif; (nHeadIdx >= 0) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; changeFace() }} **髪パーツの変更 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name changeHair ; @function ; @global ; @param actTarget {Actor} ; @param hpNewHair {HeadPart} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function changeHair(Actor actTarget, HeadPart hpNewHair) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Bool isPlayer = (Game.GetPlayer() == actTarget); Int nHeadIdx; HeadPart hpCurHair; ;------------------------------------------------------- ; Get Hair Headparts ;------------------------------------------------------- nHeadIdx = abTarget.GetIndexOfHeadPartByType(3); hpCurHair = abTarget.GetNthHeadPart(nHeadIdx); ;------------------------------------------------------- ; Reset Hair Part ;------------------------------------------------------- If !isPlayer && hpNewHair actTarget.ReplaceHeadPart(hpCurHair, hpNewHair); Elseif isPlayer && hpNewHair actTarget.ChangeHeadPart(hpNewHair); Endif; !isPlayer && hpNewHair ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; changeHair() }} *プレイヤーのヘッド・トラッキング 指定したオブジェクトに対して、プレイヤーにヘッド・トラッキングさせる処理。 自分自身を対象に指定すると、ゆっくり正面を向く。(ヘッド・トラッキングは継続) ヘッド・トラッキングそのものを停止すると、急に正面を向く。 #highlight(linenumber,php){{ Bool Function startPlayerHeadTracking(ObjectReference obrfTarget) ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Player Head Tracking ;------------------------------------------------------- If obrfTarget actPlayer.SetHeadTracking(true); actPlayer.SetLookAt(obrfTarget, false); actPlayer.SetAnimationVariableBool("bHeadTrackSpine", false); actPlayer.SetAnimationVariableInt("IsNPC", 1); Endif; obrfTarget ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; startPlayerHeadTracking() Bool Function stopPlayerLooking() ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Player Head Tracking ;------------------------------------------------------- actPlayer.SetLookAt(actPlayer, false); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; stopPlayerLooking() Bool Function stopPlayerHeadTracking() ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Player Head Tracking ;------------------------------------------------------- actPlayer.ClearLookAt(); actPlayer.SetAnimationVariableBool( \ "bHeadTrackSpine", false ); actPlayer.SetAnimationVariableInt("IsNPC", 0); actPlayer.SetHeadTracking(false); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; stopPlayerHeadTracking() }} *配列の要素数を128を超えたものを扱いたい、または動的に配列を宣言したい Papyrusの配列は通常は1~128までの範囲しか扱う事ができず、 それ以上の配列数を宣言しようと128を超える数値を指定するとエラーが発生します。 また、配列宣言時に配列数の部分を変数で指定した場合もエラーが発生します。 **NGパターン #highlight(linenumber,php){{ Actor[] NPCArray int[] ValArray Function Test() int num = 0 NPCArray = new Actor[200] ;1~128までの範囲で宣言しろとエラーが出る ; 変数の値分の配列を宣言したいが ; 配列数の指定に変数を指定するなとエラーが出る num = 200 ValArray = new int[num] EndFunction; Test() }} 配列数が128を超える配列を作成したい場合、 Float、Int、String、Form、Alias型の場合は SKSEのPapyrus拡張関数である Utility.Create~Arrayを 用いる事で配列数が128を超える配列を作成することができます。(~の部分は型によって異なりますのでスクリプトリファレンスを確認してください。) https://www.creationkit.com/index.php?title=Utility_Script Actor型の場合は標準のSKSEの拡張関数には存在しないため Papyrus拡張SKSEプラグインであるPapyrusUtilが必要となります。 この方法で配列の宣言をする場合、処理に必要な配列数を動的に宣言できるようになる他に第2引数に値を指定する事で宣言と同時に各要素の値を指定値で初期化する事ができます。 **実装例 #highlight(linenumber,php){{ Actor[] NPCArray int[] ValArray Function Test() int num = 200 ; NPCArray = PapyrusUtil.ActorArray(num, None) ValArray = Utility.CreateIntArray(num, 10) ; 配列の各要素の値を10で初期化 EndFunction; Test() }} *装備してる武器や防具に付いているエンチャントを取得する(自分で付呪したものも取得する方法) 装備してる武器、防具のエンチャントを取得する処理。 WeaponとArmorのGetEnchantment関数は自分から付呪を施したエンチャントを取得できないため、 WornObject.GetEnchantment関数でのチェックも行う。 #highlight(linenumber,php){{ ; 装備してる武器のエンチャントを取得 ; iRightHandに0を渡せば左手武器のエンチャントを取得 Enchantment Function GetWeaponEnchantment(Actor akTarget, int iRightHand = 1) Weapon w = akTarget.GetEquippedObject(iRightHand) as Weapon if !w return None endif Enchantment ench = w.GetEnchantment() if ench return ench endif return WornObject.GetEnchantment(akTarget, iRightHand, 0) EndFunction ; 装備してる防具のエンチャントを取得 Enchantment Function GetArmorEnchantment(Actor akTarget, int iSlotMask) Armor arm = akTarget.GetWornForm(iSlotMask) as Armor if !arm return None endif Enchantment ench = arm.GetEnchantment() if ench return ench endif return WornObject.GetEnchantment(akTarget, 0, iSlotMask) EndFunction }} 上記サンプルのGetArmorEnchantment関数で第ニ引数に渡す値はスロットマスクとなります。 スロットマスクの値は[[こちら>https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor]]のページを参照してください。 最初からエンチャントが付いてる装備については上記のようにGetEquippedObjectやGetWornForm等でアイテムを取得し それに対してGetEnchantment関数を使用すれば取得可能です。 ただし、GetEquippedObject等で取得できる武器と防具のデータはベースフォームであり、 自分から付呪を行った武器や防具に対してGetEnchantment関数を使用した場合はNoneを返します。 自分から付呪を行った武器や防具のエンチャントを取得したい場合は WornObjectオブジェクトのGetEnchantment関数を使用すれば取得できます。 ただし、WornObjectオブジェクトのGetEnchantment関数は &color(#F54738){対象の装備に自分で付呪したエンチャントがある場合のみ、エンチャントを返す}という仕様のため 逆に最初からエンチャントが付いてる装備の場合はNoneを返します。 そのため、現在装備しているアイテムのエンチャントを取得する場合は サンプルのように最初から付呪がされてるアイテムであるか確認し、 そうでない場合、WornObjectオブジェクトのGetEnchantment関数で自前で作成した付呪を取得するようにしましょう。 *MCMの設定を保存、ゲーム開始時や再開時に設定を自動読込する 外部ファイルにMCMの設定を保存する事でニューゲーム時や違うセーブデータでゲームを再開した時に 一々MCMの再設定をする手間を省かせるようにする。 現状でメジャーなのは[[PapyrusUtil>https://skyrimspecialedition.2game.info/detail.php?id=13048]]のJsonUtilオブジェクトを使用する方法か [[MCMHelper(SEのみ)>https://skyrimspecialedition.2game.info/detail.php?id=53000]]を使用する方法だが、 PapyrusUtilによる方法を記載する。 #highlight(linenumber,php){{ Scriptname [スクリプト名] extends ski_configbase GlobalVariable Property SampleGV auto ~省略~ String settings_path = "..\\[Mod名]\\UserSettings" ; MCMはセーブからのロード時にOnGameReloadイベントが発生するため ; この時に設定の読み込みを行う Event OnGameReload() parent.OnGameReload() LoadUserSettingsPapyrus() endEvent ; MCMを閉じた時に実行 ; 設定を自動保存 Event OnConfigClose() parent.OnConfigClose() SaveUserSettingsPapyrus() EndEvent ; MCMの初期化 ; ニューゲーム時に設定の自動読込が実行される event OnConfigInit() ;MCMの初期設定を記述 ~省略~ LoadUserSettingsPapyrus() endEvent Bool function SaveUserSettingsPapyrus() JsonUtil.SetPathIntValue(settings_path, "Sample", SampleGV.GetValueInt()) if !JsonUtil.Save(settings_path, false) debug.Trace(ModName + ": Error saving user settings.", 0) return false endIf return true endFunction Bool function LoadUserSettingsPapyrus() if !JsonUtil.IsGood(settings_path) ShowMessage(ModName + ": Can't load User Settings. Errors: {" + JsonUtil.getErrors(settings_path) + "}") return false endIf SampleGV.SetValueInt(JsonUtil.GetPathIntValue(settings_path, "Sample", SampleGV.GetValueInt())) return true endFunction ~OnConfigOpen等のMCMの各イベントの処理の記述は省略~ }} MCMを開いて後に閉じた後に (ルートディレクトリ)\SKSE\Plugins\[Mod名]ディレクトリにUserSettings.jsonが生成されている事、 セーブからのロードやニューゲーム時にMCMの設定が引き継がれている事を確認できたら、 MCMの自動保存と自動読込の作成は完了となります。 (MO2の場合は\overwrite\SKSE\Plugins\[Mod名]ディレクトリにUserSettings.jsonが生成されます。) ・上記サンプルでUserSettings.jsonに記載される内容 #highlight(linenumber,php){{ { Sample:[設定値(整数)] } }} ゲーム開始時にModの設定を一つ一つ手動でロードするのはユーザー側にとってかなり手間となるため 基本的には自動読込は実装するようにしましょう。 保存に関しては設定の切り替えが簡単だったり項目数が少なければ自動保存で問題ありませんが、 設定項目がかなりの数がある場合は自動保存はMCM側の設定で実行するか制御できるようにし、 手動保存と手動読込の手段も一緒に追加すると良いです。 余裕があれば自動保存、自動読込は設定で制御できるようにすると良いでしょう。
この項目ではやりたいことから調べられます。 やり方は一例であって必ずしもということはないので、他のパターンもあればぜひ記載してください。   #contents *ゲーム開始時に魔法やアイテムを自動で追加したい(初期化クエスト) MOD導入後に自動的にカスタマイズメニューの魔法やアイテムを追加するMODがあります。それのやり方です。 MOD導入後にセーブを読み込んだあとに一回しか動かないクエストです。 例としてカスタマイズメニューの魔法をプレイヤーに追加するクエストを作ります。 まず、Object WindowのCharacterツリーのQuestを選択します。 AchievementsQuestというのがあるので右クリック->Duplicateでコピー。 新規作成だとうまくいかない場合もあるので使えるクエストをコピーします。 コピーしたクエストを開いてIDと名前をつけます。(例:InitQuest) Priorityは99など高い数値にしておきます。 Start Game EnabledとRun at Onceにチェック。 そのままScriptsのボタンに移動して、要らないスクリプトをすべてRemove。 Addボタン->[New Script]でスクリプト新規作成。 extendsはQuest。 Spell property CustomMenuSpell(魔法名) auto Event oninit() Actor player = game.getplayer() if !(player.hasspell(CustomMenuSpell)) player.addspell(CustomMenuSpell) self.stop() endif endEvent Oninitは初期化した時に駆動するイベントです。 Oninitは同タイミングで二回読むっぽいので、重複防止のためにCustomMenuSpellを持っていない場合でしかプレイヤーに魔法を追加しません。 Run at Onceのため一度しか起動しないですが、念のためself.Stop()でクエストを止めます。 oninitはActorにつけても動きませんクエストで駆動するようにしましょう。 **ゲームロード度に動くスクリプト Oninitだと一回だけで、OnLoadは正しく動作しないのでOnPlayerLoadGameを使います(skyrim1.6以上必要)。 ただこれはActorのPlayerにしか返さないので、直接QuestにスクリプトをつけるのではなくAlias(エイリアス)を経由させて使います。 まず、Object WindowのCharacterツリーのQuestを選択します。 AchievementsQuestというのがあるので右クリック->Duplicateでコピー。 コピーしたクエストを開いてIDと名前をつけます。(例:LoadQuest) Priorityは99など高い数値にしておきます。 Start Game Enabledにチェックが入ってるか確認。 そのままScriptsのボタンに移動して、スクリプトをすべてRemove。 Addボタン->[New Script]でスクリプト新規作成。 extendsはQuest。例ではTestLoadScriptと名前をつけました。 Scriptname TestLoadScript extends Quest Event OnInit() ; 自前の関数 LoadFunc() EndEvent ;実際の処理はここに書く Function LoadFunc() debug.notification("Hello Work!") EndFunction コンパイルしてスクリプトのウィンドウを閉じる。 Quest Aliasesボタン->右クリック->New Referense Alias Aliasはplayerとでもしておく。 Unique ActorからPlayerを選ぶ。 ScriptのところをAddボタン->[New Script]でスクリプト新規作成。 TestLoadScript Property QuestScript Auto ; ↑QuestScript名 Event OnPlayerLoadGame() QuestScript.LoadFunc() EndEvent コンパイルしてスクリプトのウィンドウを閉じる。 Propertyで当クエストの指定も忘れずに。 *常時稼動させるスクリプト &bold(){※できる限り使わないことを考えぬいてください。} 常時稼働する事自体スクリプト(Papyrus)かそうでないかにかかわらず負荷がかかるために避けたほうが良いです。 とくに&bold(){間隔の短い}OnUpdateによるスクリプト回しは、環境にもよりますがスタックエラーにつながりやすいです。 OnUpdateとRegisterForSingleUpdate()を使います。 RegisterForUpdate()はスタックエラーの原因になりやすいので使いません。 つけたり止めたりしやすいのでクエストかマジックエフェクトで回します。 クエストの作り方は上記2つに書いてあるので省略。 例はプレイヤーがスニーク中かつ灯火使用中の場合は灯火を消します。 ;(マジックエフェクトの場合はEvent OnEffectStart) Event Oninit() RegisterForSingleUpdate(10) EndEvent Event OnUpdate() ;実際の処理はここ player = game.getplayer() if (player.isSneaking()) && (player.HasSpell(Candlelight)) DispelSpell(Candlelight) endif ;10秒後OnUpdate開始。つまり10秒単位で回る。 RegisterForSingleUpdate(10) EndEvent に1秒など短い間隔でループさせると大量にスタックしてCTDの要因や他のスクリプトの遅延になります。 (OnUpdate内の記述によりますが)できれば2秒以上ゆとりを持って設定しましょう。 高速でループさせる必要があるかどうかをよく吟味して、何か単発のイベントで代替できるか探りましょう。 *DLCや特定のespが読み込まれてる場合に処理をする GetFormFromFile()だと存在しない場合にログにエラーが出てしますのでGetModByName()を使います。 例ではドーンガード。 if Game.GetModByName("Dawnguard.esm") < 255 ; ドーンガードがある場合の処理。別のespのフォームIDからFormを取得するには Game.GetFormFromFile(0x00000000,"Dawnguard.esm") else ;GetModByNameが255を返した場合読み込まれていません ;ドーンガードがない場合の処理 endIf *一度のみ実行 Oninit()などで絶対に一度のみしか走らないで欲しい処理などに Bool doOnce = False Event Oninit() if (doOnce) return else doOnce = True endif ;実際の処理はここ EndEvent *名前 **アクターの名前の取得 対象アクターの名前を取得して左上に表示させたい時に、 debug.notification("ActorName:" + Actor) とやってもスクリプト名やActorと表示されるだけなので、以下のようにアクターベースから名前を取得します。 debug.notification("ActorName:"+ Actor.GetActorBase().GetName()) #highlight(linenumber,php){{ Actor actTarget; String szName; ; 現在のゲーム上で表示されている名前 szName = actTarget.GetDisplayName(); ; 表示名の変更に影響されない元の設定名 ; レベルドリストから生成されたアクターの場合、空白になる場合がある。 szName = actTarget.GetActorBase().GetName(); ; 表示名の変更に影響されない元の設定名 ; レベルドリストから生成されたアクターの場合でも、元になったActorBaseの名前が入る。 ; 通常のアクターの場合でも使えるので、設定名を取得する場合はこの方が確実。 szName = actTarget.GetLeveledActorBase().GetName(); }} **場所の名前の取得 場所の名前をセル→ロケーション→ワールドの順に取得する。 屋外などでは、セルやロケーションに名前が設定されていない場合がある為。 celPlaceで指定したセルか、objrefMarkerで指定したリファレンスの場所名を返す。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getPlaceName ; @function ; @global ; @param [celPlace=None] {Cell} ; @param [objrefMarker=None] {ObjectReference} ; @returns {String} ; ---------------------------------------------------------- String Function getPlaceName( \ Cell celPlace = None, \ ObjectReference objrefMarker = None \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Location locMarker; WorldSpace wsMarker; String szName = ""; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !celPlace && objrefMarker celPlace = objrefMarker.GetParentCell(); Elseif celPlace && !objrefMarker objrefMarker = celPlace.GetNthRef(0); Endif; !celPlace && objrefMarker ;------------------------------------------------------- ; Get Cell Name ;------------------------------------------------------- If celPlace szName = celPlace.GetName(); Endif; celPlace ;------------------------------------------------------- ; Get Location Name ;------------------------------------------------------- If (szName == "") If objrefMarker locMarker = objrefMarker.GetCurrentLocation(); EndIf; objrefMarker If locMarker szName = locMarker.GetName(); Endif; locMarker Endif; (szName == "") ;------------------------------------------------------- ; Get World Name ;------------------------------------------------------- If (szName == "") If objrefMarker wsMarker = objrefMarker.GetWorldSpace(); EndIf; objrefMarker If wsMarker szName = wsMarker.GetName(); Endif; wsMarker Endif; (szName == "") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return szName; EndFunction; getPlaceName() }} *espファイルのゴミを消す 一度設定してしまったプロパティはespに保存されます。 その後、不要になった場合にそのまま消してしまうとPapyrus.logに以下のようなwarningメッセージを吐くespが出来上がります。 [12/24/2020 - 00:00:01PM] warning: Property <プロパティ名> on script <スクリプト名> attached to <オブジェクトのEDID> (オブジェクトのformID) cannot be initialized because the script no longer contains that property espが自身に保存されたプロパティ情報を元に初期化しようとしたがスクリプトには存在しないために出る注意文で、新規ゲームでも関係なくメッセージを出します。 実害は無いですが邪魔な存在である事は確かなのでなるべくなら消しましょう。 -消し方 #ref(clear_value.jpg) 消したいプロパティを選択後clear valueを押し<<defalult>>にする事でespから情報を削除出来ます。 設定後、CKを保存してespを更新するのを忘れないでください。 更新を忘れて、スクリプトからプロパティを削除してしまうと注意文は消えません。 消してしまった場合、一度スクリプトにプロパティを加えてコンパイルし直してください。 なお、これはセーブデータに残ってしまった情報とは違います。 こういうの↓ 12/24/2020 - 01:00:00PM] warning: Could not find type <オブジェクトのEDIDやAlias> in the type table in save これはセーブデータに残ってしまった情報で確実な方法は発見されていません(2013/07/04現在) セーブデータに残ったスクリプト情報の残骸は[[Save game script cleaner>http://www.nexusmods.com/skyrim/mods/52363/]]というツールで消せます。 *扉の開錠/施錠 -サンプル: &ref(ExampleUnlockDoor.zip)(動かし方は中にある readme.txt を参照のこと) 扉の施錠判断には [[ObjectReference.IsLocked>http://www.creationkit.com/index.php?title=IsLocked_-_ObjectReference]] を使います。扉の開錠/施錠には [[ObjectReference.Lock>http://www.creationkit.com/index.php?title=Lock_-_ObjectReference]] を使います。 以下は、アクティベートイベントを元に、対象の扉が開いている場合は施錠/閉じている場合は解錠するスクリプトの例です。 Scriptname ExampleUnlockDoor extends ObjectReference ObjectReference Property targetDoor Auto Event OnActivate(ObjectReference akActionRef) if (targetDoor.IsLocked()) Debug.Notification("Unlock") targetDoor.Lock(false) else Debug.Notification("Lock") targetDoor.Lock(true) endIf endEvent targetDoor プロパティに事前に対象となる扉を指定する必要があります。 また、Creation Kit にて事前に扉の状態を決めるには Lock タブにある情報を設定します。 以下、スクリーンショットでは鍵を必要とする状態で扉を閉めています。 #ref(set_lock.png) 何らかのイベント等をトリガーに先に進めるようにする場合などに使えます。 *オブジェクトの移動 -サンプル: &ref(ExampleMoveObject.zip)(動かし方は中にある readme.txt を参照のこと) 設置済みのオブジェクトを移動させるには [[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] を使います。 以下はアクティベートした対象そのものを後ろへ移動させるスクリプトの例です。 Scriptname ExampleMoveObject extends ObjectReference Event OnActivate(ObjectReference akActionRef) SetPosition(x, y - 128, z) endEvent [[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] で指定する座標は、セル内の絶対座標系の値です。 [[ObjectReference>http://www.creationkit.com/index.php?title=ObjectReference_Script]] には現在位置を表す x, y, z プロパティがあるので、それを使って相対位置を指定するのが常套手段です。 向きも変えたい場合は [[ObjectReference.SetAngle>http://www.creationkit.com/index.php?title=SetAngle_-_ObjectReference]] を併用します。 ''※ポイント'':この移動方法は、今の座標から徐々に指定の場所へ移動させるものではありません。[[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] は、瞬時の移動しかできません。徐々に移動させるには前提として対象が [[Actor>http://www.creationkit.com/index.php?title=Actor_Script]] である必要があります。 *新しいオブジェクトの設置 **簡単な例 -サンプル: &ref(ExampleSetObject.zip)(動かし方は中にある readme.txt を参照のこと) オブジェクトを新しく設置するには [[ObjectReference.PlaceAtMe>http://www.creationkit.com/index.php?title=PlaceAtMe_-_ObjectReference]] を使います。 第一引数に [[Form>http://www.creationkit.com/index.php?title=Form_Script]] オブジェクトを指定します。 オブジェクト指向熟練者への補足ですが、以下の対比がちょうど当てはまると覚えておくと良いでしょう。 |[[Form>http://www.creationkit.com/index.php?title=Form_Script]] オブジェクト|クラス| |[[ObjectReference.PlaceAtMe>http://www.creationkit.com/index.php?title=PlaceAtMe_-_ObjectReference]]|new 演算子| |[[ObjectReference>http://www.creationkit.com/index.php?title=ObjectReference_Script]] オブジェクト|インスタンス| 以下(ExampleSetObject サンプル)はアクティベートする度に WETempActivator が後ろに増えていくスクリプトの例です。 Scriptname ExampleSetObject extends ObjectReference Activator Property setObject Auto int count = 0 Event OnActivate(ObjectReference akActionRef) count = count + 1 ObjectReference newObject = PlaceAtMe(setObject, 1) newObject.SetPosition(x, y - count * 128, z) endEvent サンプルは WETempActivator を新しく設置するものですが、他のものでも Form オブジェクトがあれば新しいオブジェクトを生成できます。 **スキーヴァーを大量発生させる例 -サンプル: &ref(ExampleSetSkeever.zip)(動かし方は中にある readme.txt を参照のこと) アクティベートするたびにスキーヴァーが部屋のどこかに設置されるサンプルです。 Scriptname ExampleSetSkeever extends ObjectReference ActorBase Property skeever Auto Event OnActivate(ObjectReference akActionRef) float newx = -1000 + 2000 * Utility.RandomFloat() float newy = -1000 + 2000 * Utility.RandomFloat() Actor newActor = PlaceAtMe(skeever, 1) as Actor newActor.SetPosition(newx, newy, z) newActor.StopCombat() endEvent skeever プロパティには「EncSkeever」が設定してあります。何度もアクティベートすれば当然…。 #ref(skeevers.jpg) 囲まれます。 **SpawnerTaskを使用して大量発生させる SpawnerTaskを使えば、敵やオブジェクトを効率よくスポーンさせることが出来ます。 下記の例では、PCの周囲に一気に出現させています。 #highlight(linenumber,php){{ Bool Function spawnAroundPlayer(Form frmTarget, Int nAddNum = 1) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); Float[] aPos = new Float[3]; Float[] aRot = new Float[3]; Int nTaskID; ;------------------------------------------------------- ; Spawn ;------------------------------------------------------- If frmTarget && (nAddNum > 0) nTaskID = SpawnerTask.Create(); SpawnerTask.AddSpawn( \ nTaskID, frmTarget, actPlayer, aPos, aRot, nAddNum); SpawnerTask.Run(nTaskID); Endif; frmTarget && (nAddNum > 0) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; spawnAroundPlayer() }} **FormList を使ったランダム設置の例 -サンプル: &ref(ExampleSetRandomObject.zip)(動かし方は中にある readme.txt を参照のこと) アクティベートするたびに [[FormList>http://www.creationkit.com/index.php?title=FormList_Script]] の中にある何かが部屋のどこかに設置されるサンプルです。 FormList には ObjectWindow の「WorldObjects/Tree/Plants」カテゴリにある植物群が入っており、設置後、素材を採集できます。 Scriptname ExampleSetRandomObject extends ObjectReference FormList Property flowers Auto Event OnActivate(ObjectReference akActionRef) float newx = -1000 + 2000 * Utility.RandomFloat() float newy = -1000 + 2000 * Utility.RandomFloat() Debug.Notification("Activate ... new object (" + newx + ", " + newy + ")") ObjectReference newObject = PlaceAtMe(flowers.GetAt(Utility.RandomInt(0, flowers.GetSize() - 1)), 1) newObject.SetPosition(newx, newy, z) endEvent *アクターの移動 **アクターの誘導 -サンプル: &ref(ExampleSkeeverAroundStone.zip)(動かし方は中にある readme.txt を参照のこと) アクターとは動きまわるオブジェクトのことです。プレイヤーや敵やNPCなどが該当します。 「オブジェクトの移動」で紹介した [[ObjectReference.SetPosition>http://www.creationkit.com/index.php?title=SetPosition_-_ObjectReference]] は瞬時の移動です。誘導には [[Actor.PathToReference>http://www.creationkit.com/index.php?title=PathToReference_-_Actor]] を使います。 誘導する際、移動先のオブジェクトを指定する必要があります。[[ObjectReference>http://www.creationkit.com/index.php?title=ObjectReference_Script]] であれば何でも良いですが、もしも自由に誘導させたいならば XMarker を使うのがお勧めです。 Scriptname ExampleSkeeverAroundStone extends ObjectReference Actor Property skeever Auto ObjectReference Property destination Auto int angle = 0 Event OnCellLoad() RegisterForSingleUpdate(1) endEvent Event OnUpdate() float distance = skeever.GetDistance(destination) if (distance < 64) angle = angle + 45 destination.SetPosition(x + 256 * Math.cos(angle), y + 256 * Math.sin(angle), z) endIf skeever.PathToReference(destination, 0.5) RegisterForSingleUpdate(1) endEvent 以上のサンプルはスキーヴァーが石碑の周りをぐるぐると回らせるものです。 destination に XMarker をセットし、スキーヴァーの到着を判断して位置を変えていきます。 また、本サンプルは「常時稼動させるスクリプト」のサンプルにもなっています。 **隊列の形成 KeepOffsetFromActor()を使えば、対象のアクターを別のアクターの隣や背後といった定位置に移動させることが出来る。 隊列の形成などは、この手法が使われていることが多い。 #highlight(linenumber,php){{ Actor actTarget; Actor actFollow; actTarget.KeepOffsetFromActor( \ actFollow, \ 170.0, -170.0, 0.0, \ afCatchUpRadius = 260.0, \ afFollowRadius = 8.0 ); }} *倒した敵からコンテナードロップ -サンプル: &ref(ExampleDropChest.zip)(動かし方は中にある readme.txt を参照のこと) 敵を倒した際、コンテナーをドロップさせてアイテムをゲットできるようにしてみましょう。 方法は簡単です。[[Actor.OnDeath>http://www.creationkit.com/index.php?title=OnDeath_-_Actor]] イベントで敵が倒されたタイミングを見計らい -コンテナーを設置 -敵を排除 するだけです。以下、事例です。 Scriptname ExampleDropChest extends ObjectReference Container Property BaseChest Auto Event OnDeath(Actor akKiller) ObjectReference chest = PlaceAtMe(BaseChest, 1) Disable(false) Delete() chest.SetAngle(0, 0, chest.z) EndEvent BaseChest に事前にドロップさせるコンテナーをセットしておく必要があります。 コンテナーを設置したら、敵本体を即時に無効にして削除します。 サンプルではスキーヴァー(EncSkeever)がフィールドにいます。倒すと空の宝箱(TreasDraugrChestEMPTYSmall)がドロップします。 最後にアングルを変えているのは、敵が死亡時、横たわるのでそれに合わせて宝箱も傾いてしまうのを補正するためです。 *キルムーブ時のイベントの取得 要:SKSE Event Init() RegisterForCameraState();まず別のイベントで登録する EndEvent Event OnPlayerCameraState() if newState == 2 ;キルムーブのカメラ切り替え EndIf EndEvent *NPCのインベントリから見えない装備を外す方法 単に今着ている装備を外すだけだと、ロードを挟んだ際に元々着ていた服に戻ります。 これを防ぐためには透明な装備を登録したOutfitをSetすればよいです。 価値を0にしておけば別の装備を渡したときにそちらを優先してきてくれます。 なおOutfitをSetすると元の服装に戻らなくなるため、予め回収しておくのもよいでしょう。 Outfit Property NakedOutfit Auto ; AAの無い装備を一つ登録したOutfit Function ExampleFunction(Actor akNPC) ; 単に今着ている装備を外したい場合 akNPC.UnequipAll() akNPC.RemoveAllItems() ; アイテムを回収したい場合は自分を指定 ; akNPC.RemoveAllItems(Game.GetPlayer()) ; 恒久的に装備を外してしまいたい場合 akNPC.SetOutfit(NakedOutfit) EndFunction *ラグドール状態の取得 ラグドールはフスロダなどで吹っ飛んでいるとき、または死亡時の手足がブラブラした状態のことです。 判定方法は二通りありますが片方はアクターが『フロスダやマヒ等で倒れているか』を判定する時に使い、 もう片方はオブジェクトが重力の影響下や固定されてるオブジェであるかの判定に使う事を推奨します。(理由は後述) **powerofthree's Papyrus ExtenderのGetActorKnockState関数を使う こちらは対象がアクターでより正確にラグドール状態を取得したい場合に有効となります。 GetActorKnockState関数はアクターがフロスダやマヒ(あるいはPushActorAway)等で倒れてラグドール状態になり、起き上がりが終わるまでは0以外を返します。 ただし、あくまで死亡以外の要因でラグドール状態になった時に判定するためのもので、死亡が原因でラグドールになった場合はGetActorKnockStateは0を返すためIsDeadによるチェックも必要です。 (マヒ状態や吹っ飛んでる途中で死亡した場合はGetActorKnockStateは0以外を返します) #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isInRagdollActor ; @desc 対象の人物がフロスダ等によるラグドール影響下にあるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @param isDeadRagdollCheck {Bool} 死亡からラグドール状態に移行した場合を考慮するか ; @returns {Bool} 影響下にあればtrue ; ---------------------------------------------------------- Bool Function isInRagdollActor(Actor actTarget, Bool isDeadRagdollCheck = true) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isInRagdoll = false; ;------------------------------------------------------- ; Checking Ragdoll Status ;------------------------------------------------------- If actTarget ; 死亡時にラグドール状態に移行する場合はGetActorKnockStateは0を返すため ; 従来のGetMass関数による判定と同じ挙動を行いたい時はIsDeadのチェックを加える If (PO3_SKSEFunctions.GetActorKnockState(actTarget) != 0) isInRagdoll = true ElseIf isDeadRagdollCheck isInRagdoll = actTarget.IsDead() EndIf Endif; actTarget ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isInRagdoll; EndFunction; isInRagdollActor() }} **GetMass関数を使う(従来のやり方、SE版ではアクターに対しては非推奨) アクターが吹っ飛んでいるかの判定では最もメジャーな方法でしたが、[[Precision>https://www.nexusmods.com/skyrimspecialedition/mods/72347]]の登場により事情が変わりました。 このMod環境下では武器攻撃の判定をより正確な判定にするのにHavok衝突判定を使うため、アクターに対してアクティブラグドールというものを付与されるようになります。 その影響でこのModの環境下の場合はアクターがラグドール状態でなくてもGetMassが0以外を返すようになります。 そのため、Precision環境下でアクターがふっ飛ばされてラグドールになってるかをGetMass関数で判定すると問題が発生する可能性があるため注意が必要です。 Precision環境でない場合はアクターに対するGetMass関数は通常時は常に0を返しますが、ラグドール中だけは設定されているラグドールの計算に使う重量(Mass)を返します。 ※フロスダなどで吹っ飛んでから静止すると起き上がりモーションが発生しますが、このモーション中もラグドール扱いとして0以外を返します。 Precision環境下でなくても今後出てくるModによってはGetMass関数がアクターに対して通常時でも0以外を返すようになる可能性があるため前述のGetActorKnockState関数でラグドール判定を行う事を推奨します。 例: if Game.GetPlayer().GetMass() == 0 ;ラグドール中じゃないとき debug.SendAnimationEvent(Game.GetPlayer(),"staggerStart") endif #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isInRagdoll ; @desc 対象の人や物がラグドール影響下にあるかを返す ; @function ; @global ; @param obrfThis {ObjectReference} 対象の人や物 ; @returns {Bool} 影響下にあればtrue ; ---------------------------------------------------------- Bool Function isInRagdoll(ObjectReference obrfThis) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isInRagdoll = false; ;------------------------------------------------------- ; Checking Ragdoll Status ;------------------------------------------------------- If obrfThis && (obrfThis.GetMass() != 0) isInRagdoll = true; Endif; obrfThis && (obrfThis.GetMass() != 0) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isInRagdoll; EndFunction; isInRagdoll() }} **ラグドール中のモーション再生によるバグ ラグドール中あるいは起き上がってる途中に別のモーションを再生すると、その場で固定されて行動不能になるバグがあります。ラグドール中は基本的に別のモーションでは割り込めないですが、ドラウグルはよろめき(StaggerStart)が割り込めてしまうので、上記の条件で除外しましょう。 *十進数と十六進数の変換 Papyrusには、こういった標準的な関数がないので不便する場合がある。 **十進数→十六進数 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name convDecToHex ; @desc 整数値を十六進数表記の文字列に変換 ; @function ; @global ; @param nNum {Int} 変換したい整数値 ; @returns {String} 十六進数表記の文字列 ; ---------------------------------------------------------- String Function convDecToHex(Int nNum) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- String szHex; Bool isNegative; Int[] aNums = new Int[8]; String[] aHex = new String[8]; Int nIdx = 0; Int nLen = aNums.Length; ;------------------------------------------------------- ; Checking over 0x80000000 ;------------------------------------------------------- If (nNum < 0) isNegative = True; nNum += 0x80000000; Endif ;------------------------------------------------------- ; Parse integer ;------------------------------------------------------- aNums[0] = (nNum / 0x10000000); nNum -= (aNums[0] * 0x10000000); aNums[1] = (nNum / 0x01000000); nNum -= (aNums[1] * 0x01000000); aNums[2] = (nNum / 0x00100000); nNum -= (aNums[2] * 0x00100000); aNums[3] = (nNum / 0x00010000); nNum -= (aNums[3] * 0x00010000); aNums[4] = (nNum / 0x00001000); nNum -= (aNums[4] * 0x00001000); aNums[5] = (nNum / 0x00000100); nNum -= (aNums[5] * 0x00000100); aNums[6] = (nNum / 0x00000010); nNum -= (aNums[6] * 0x00000010); aNums[7] = nNum; ;------------------------------------------------------- ; Convert to HEX ;------------------------------------------------------- If isNegative aNums[0] = aNums[0] + 8; Endif While (nIdx < nLen) If (aNums[nIdx] == 10) aHex[nIdx] = "A"; Elseif (aNums[nIdx] == 11) aHex[nIdx] = "B"; Elseif (aNums[nIdx] == 12) aHex[nIdx] = "C"; Elseif (aNums[nIdx] == 13) aHex[nIdx] = "D"; Elseif (aNums[nIdx] == 14) aHex[nIdx] = "E"; Elseif (aNums[nIdx] == 15) aHex[nIdx] = "F"; Else aHex[nIdx] = (aNums[nIdx] as String); Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Join characters ;------------------------------------------------------- szHex = aHex[0] + aHex[1] + aHex[2] + aHex[3] \ + aHex[4] + aHex[5] + aHex[6] + aHex[7]; ;------------------------------------------------------- ; Return ;------------------------------------------------------- return szHex; EndFunction; convDecToHex() }} **十六進数→十進数 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name convHexToDec ; @desc 十六進数表記の文字列を整数値で応答 ; @function ; @global ; @param szHex {String} 十六進数表記の文字列 ; @returns {Int} 変換した整数値 ; ---------------------------------------------------------- Int Function convHexToDec(String szHex) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nIdx = 0; Int nLen = StringUtil.GetLength(szHex); String cChar; Int nNum; Int nRet; ;------------------------------------------------------- ; Parse Hex-String ;------------------------------------------------------- While (nIdx < nLen) cChar = StringUtil.GetNthChar(szHex, nIdx); If StringUtil.IsDigit(cChar) nNum = cChar as Int; Elseif (cChar == "A") || (cChar == "a") nNum = 10; Elseif (cChar == "B") || (cChar == "b") nNum = 11; Elseif (cChar == "C") || (cChar == "c") nNum = 12; Elseif (cChar == "D") || (cChar == "d") nNum = 13; Elseif (cChar == "E") || (cChar == "e") nNum = 14; Elseif (cChar == "F") || (cChar == "f") nNum = 15; Else nNum= 0; Endif nNum = (nNum * Math.pow(16, (nLen - (nIdx + 1)))) as Int; nRet += nNum; nIdx += 1 EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nRet; EndFunction; convHexToDec() }} *攻撃側と防御側の位置と角度判定 バックスタブなどのMODで使われる手法。二者の相対的な距離と角度から判定している。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getAttackAngle ; @desc 攻撃側と防御側の向きと距離のタイプを返す ; @function ; @global ; @param actAtk {Actor} 攻撃側の人 ; @param actDef {Actor} 防御側の人 ; @param [nCloseRange=170.0] {Float} 近接と判断する距離限界 ; @param [nFrontAngle=44.0] {Float} 正面と判断する角度限界 ; @param [nRearAngle=125.0] {Float} 背面と判断する角度限界 ; @returns {Int} ; 距離のタイプとして、以下を返す ; -1 : 以下のどれでもない、または処理エラー ; 11 : 正面近距離 ; 12 : 背面近距離 ; 21 : 正面遠距離 ; 22 : 背面遠距離 ; ---------------------------------------------------------- Int Function getAttackAngle( \ Actor actAtk, \ Actor actDef, \ Float nCloseRange = 170.0, \ Float nFrontAngle = 44.0, \ Float nRearAngle = 125.0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nRet = -1; Float nFaceMin; Float nFaceMax; Float nRearMin; Float nRearMax; Float nDist; Float nAngleAtk; Float nAngleDef; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !actAtk || !actDef || (actAtk == actDef) return nRet; Endif; !actAtk || !actDef || (actAtk == actDef) If (nCloseRange < 0.0) nCloseRange *= -1.0; Endif; (nCloseRange < 0.0) If (nFrontAngle < 0.0) nFrontAngle *= -1.0; Endif; (nFrontAngle < 0.0) If (nFrontAngle > 90.0) nFrontAngle = 90.0; Endif; (nFrontAngle > 90.0) If (nRearAngle < 0.0) nRearAngle *= -1.0; Endif; (nRearAngle < 0.0) If (nRearAngle < 90.0) nRearAngle = 90.0; Elseif (nRearAngle > 180.0) nRearAngle = 180.0; Endif; (nRearAngle < 90.0) ;------------------------------------------------------- ; Init Range Values ;------------------------------------------------------- nFaceMin = nFrontAngle * -1.0; nFaceMax = nFrontAngle; nRearMin = nRearAngle * -1.0; nRearMax = nRearAngle; ;------------------------------------------------------- ; Get Distance and Angles ;------------------------------------------------------- nDist = actAtk.GetDistance(actDef); nAngleAtk = actAtk.GetHeadingAngle(actDef); nAngleDef = actDef.GetHeadingAngle(actAtk); ;------------------------------------------------------- ; Check Distance ;------------------------------------------------------- If (nDist <= nCloseRange) nRet = 10; Else nRet = 20; Endif; (nDist <= nCloseRange) ;------------------------------------------------------- ; Check Defender Angle ;------------------------------------------------------- If (nAngleAtk >= nFaceMin) && (nAngleAtk <= nFaceMax) If (nAngleDef >= nFaceMin) \ && (nAngleDef <= nFaceMax) nRet += 1; Elseif (nAngleDef <= nRearMin) \ || (nAngleDef >= nRearMax) nRet += 2; Else nRet = -1; Endif; (nDist <= nCloseRange) && ... Else nRet = -1; Endif; (nAngleAtk >= nFaceMin) && ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nRet; EndFunction; getAttackAngle() }} *コンソールやクロスヘアで選択した対象を取得 コンソールやクロスヘアで選択中のオブジェクトのリファレンスを取得したい場合の処理。 Game.GetCurrentConsoleRef()は昔のSKSEではサポートしていなかった関数なので、念の為にバージョンをチェックしている。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getObjectSelected ; @desc 選択中のオブジェクトを取得する ; @function ; @global ; @param [enableCrosshair=True] {Bool} ; クロスヘア選択を有効にする ; @param [enableConsole=True] {Bool} ; コンソール選択を有効にする ; @returns {ObjectReference} ; 取得した収納オブジェクト ; ---------------------------------------------------------- ObjectReference Function getObjectSelected( \ Bool enableCrosshair = True, \ Bool enableConsole = True \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ObjectReference obrfFind; Int nSkseVer = SKSE.GetScriptVersionRelease(); ;------------------------------------------------------- ; Console selected ;------------------------------------------------------- If !obrfFind && enableConsole && (nSkseVer > 47) obrfFind = \ Game.GetCurrentConsoleRef() as ObjectReference; Endif ;------------------------------------------------------- ; Crosshair ;------------------------------------------------------- If !obrfFind && enableCrosshair obrfFind = \ Game.GetCurrentCrosshairRef() as ObjectReference; Endif ;------------------------------------------------------- ; Return ;------------------------------------------------------- Return obrfFind; EndFunction; getObjectSelected() }} *ゲーム内の時間取得 Utility.GetCurrentGameTime()でゲーム内の経過日数を取得してから計算する。 **ゲーム内時間の週を数値で返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getCurrentWeekIdx ; @desc ゲーム内時間の週を数値で返す ; @function ; @global ; @returns {Int} 週を示す整数値(0 - 6) ; ---------------------------------------------------------- Int Function getCurrentWeekIdx() global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Float nGameTime = Utility.GetCurrentGameTime(); Int nDays = Math.Floor(nGameTime); Int nWeeks = Math.Floor(nDays / 7); Int nWeekIdx = nDays - (nWeeks * 7); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nWeekIdx; EndFunction; getCurrentWeekIdx() }} **ゲーム内時間の時間を数値で返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name getCurrentHour ; @desc ゲーム内時間の時間を数値で返す ; @function ; @global ; @returns {Int} 時間を示す整数値(0 - 23) ; ---------------------------------------------------------- Int Function getCurrentHour() global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Float nGameTime = Utility.GetCurrentGameTime(); Int nDays = Math.Floor(nGameTime); Int nHours = Math.Floor((nGameTime - nDays) * 24); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nHours; EndFunction; getCurrentHour() }} *判定処理 様々な判定を行う処理の例。 **対象の人が敵性であるかを返す この例では、敵と判定する為の3つの条件を組み合わせている。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isEnemy ; @desc 対象の人が敵性であるかを返す ; @function ; @global ; @param actEnemy {Actor} 対象の人 ; @param [actTarget=None] {Actor} ; 誰に対する敵かを指定(None時はプレイヤーに対する敵) ; @returns {Bool} 敵性であればtrue ; ---------------------------------------------------------- Bool function isEnemy(Actor actEnemy, Actor actTarget = None) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isEnemy = false; Int nReaction; Int nRank; ;------------------------------------------------------- ; Check Argument ;------------------------------------------------------- If !actEnemy return isEnemy; Elseif !actTarget actTarget = Game.GetPlayer(); Endif; !actEnemy ;------------------------------------------------------- ; Check Actor ;------------------------------------------------------- If (actEnemy == actTarget) return isEnemy; Endif; (actEnemy == actTarget) ;------------------------------------------------------- ; Check Hostile ;------------------------------------------------------- If !isEnemy isEnemy = actEnemy.IsHostileToActor(actTarget); Endif; !isEnemy ;------------------------------------------------------- ; Check Reaction ;------------------------------------------------------- If !isEnemy ; Obtains this actors faction-based reaction ; to the other actor ; 0 - Neutral ; 1 - Enemy ; 2 - Ally ; 3 - Friend nReaction = actEnemy.getFactionReaction(actTarget); isEnemy = (nReaction == 1); Endif; !isEnemy ;------------------------------------------------------- ; Check Relationship ;------------------------------------------------------- If !isEnemy ; Relationship functions use the following values: ; 4 - Lover ; 3 - Ally ; 2 - Confidant ; 1 - Friend ; 0 - Acquaintance ; -1 - Rival ; -2 - Foe ; -3 - Enemy ; -4 - Archnemesis nRank = actEnemy.GetRelationshipRank(actTarget); isEnemy = (nRank <= -2); Endif; !isEnemy ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isEnemy; EndFunction; isEnemy() }} **対象の人がフォロワーであるかを返す フォロワーMODを使用している場合では、判定条件が異なるかも知れない。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isFollower ; @desc 対象の人がフォロワーであるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} フォロワーであればtrue ; ---------------------------------------------------------- Bool Function isFollower(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Faction[] aFollower; Bool isFollower = false; ;------------------------------------------------------- ; Get Factions ;------------------------------------------------------- ; CurrentFollowerFaction 0x5C84E ; CurrentHireling 0xBD738 aFollower = new Faction[2]; aFollower[0] = Game.GetForm(0x5C84E) As Faction; aFollower[1] = Game.GetForm(0xBD738) As Faction; ;------------------------------------------------------- ; Check Factions ;------------------------------------------------------- If actTarget.IsInFaction(aFollower[0]) \ || actTarget.IsInFaction(aFollower[1]) isFollower = true; Endif; actTarget.IsInFaction(aFollower[0]) || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isFollower; EndFunction; isFollower() }} **対象の人が魔法使いであるかを返す この例では、戦闘スタイルで魔法使いかを判定している。 この判定条件が有効かどうかは、使う場面による。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isMagicUser ; @desc 対象の人が魔法使いであるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} 魔法使いであればtrue ; ---------------------------------------------------------- Bool Function isMagicUser(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); CombatStyle csTarget = abTarget.GetCombatStyle(); Float nMeleeMult = csTarget.GetMeleeMult(); Float nMagicMult = csTarget.GetMagicMult(); Float nRangedMult = csTarget.GetRangedMult(); Bool isMagicUser = false; ;------------------------------------------------------- ; Check ;------------------------------------------------------- If (nMagicMult > nMeleeMult) || (nMagicMult > nRangedMult) isMagicUser = true; Endif; (nMagicMult > nMeleeMult) || (nMagicMult > nRangedMult) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isMagicUser; EndFunction; isMagicUser() }} **対象の人がクリーチャーであるかを返す この例では、人間タイプでないことをクリーチャーの条件としている。 MODで、ActorTypeCreatureのキーワードがないクリーチャーが多い為。 良し悪しあるので、ケェス・バイ・ケェスではある。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isCreature ; @desc 対象の人がクリーチャーであるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} クリーチャーであればtrue ; ---------------------------------------------------------- Bool Function isCreature(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Int nSex = abTarget.GetSex(); Bool isCreature = false; ;------------------------------------------------------- ; Check Keyword ;------------------------------------------------------- If !actTarget.HasKeywordString("ActorTypeNPC") isCreature = true; Elseif (nSex == -1) isCreature = true; Endif; !actTarget.HasKeywordString("ActorTypeNPC") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isCreature; EndFunction; isCreature() }} **対象の人が人間男性であるかを返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isMale ; @desc 対象の人が人間男性であるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} 人間男性であればtrue ; ---------------------------------------------------------- Bool Function isMale(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Int nSex = abTarget.GetSex(); Bool isMale = false; ;------------------------------------------------------- ; Check Keyword ;------------------------------------------------------- If !actTarget.HasKeywordString("ActorTypeNPC") isMale = false; Elseif (nSex == 0) isMale = true; Endif; !actTarget.HasKeywordString("ActorTypeNPC") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isMale; EndFunction; isMale() }} **対象の人が人間女性であるかを返す #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isFemale ; @desc 対象の人が人間女性であるかを返す ; @function ; @global ; @param actTarget {Actor} 対象の人 ; @returns {Bool} 人間女性であればtrue ; ---------------------------------------------------------- Bool Function isFemale(Actor actTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Int nSex = abTarget.GetSex(); Bool isFemale = false; ;------------------------------------------------------- ; Check Keyword ;------------------------------------------------------- If !actTarget.HasKeywordString("ActorTypeNPC") isFemale = false; Elseif (nSex == 1) isFemale = true; Endif; !actTarget.HasKeywordString("ActorTypeNPC") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isFemale; EndFunction; isFemale() }} **対象の人が戦闘継続可能であるかを返す その瞬間に継続可能かどうかの判定。GetMass()はラグドール状態かの判定。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name canCombat ; @desc 対象の人が戦闘継続可能であるかを返す ; @function ; @global ; @param [actTarget=None] {Actor} ; 対象の人 (None時はプレイヤーが対象) ; @returns {Bool} ; 戦闘継続可能であればtrue ; ---------------------------------------------------------- Bool Function canCombat(Actor actTarget = None) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool canCombat = true; ;------------------------------------------------------- ; Checking Actor ;------------------------------------------------------- If !actTarget actTarget = Game.GetPlayer(); Endif; !actTarget ;------------------------------------------------------- ; Checking Keywords ;------------------------------------------------------- If !actTarget.Is3DLoaded() \ || actTarget.IsDisabled() \ || actTarget.IsDead() \ || actTarget.IsUnconscious() \ || actTarget.IsBleedingOut() \ || (actTarget.GetMass() != 0) canCombat = false; Endif; !actTarget.Is3DLoaded() || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return canCombat; EndFunction; canCombat() }} **パワーアタック判定 スプリント攻撃以外はアニメーション変数「bAllowRotation」で判定できる。 #highlight(linenumber,php){{ ; Animation event, sent when an object we are listening ; to hits one of the events we are listening for Event OnAnimationEvent(ObjectReference akSource, string asEventName) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Actor actTarget = akSource as Actor; Bool isPowerAtk = true; ;------------------------------------------------------- ; Check Power Attack ;------------------------------------------------------- If (asEventName == "weaponSwing") \ || (asEventName == "weaponLeftSwing") isPowerAtk = \ actTarget.GetAnimationVariableBool( \ "bAllowRotation" ); Endif; (szEvent == "weaponSwing") || ... EndEvent; OnAnimationEvent() }} ***The Ultimate Dodge Mod か Nemesis使用環境の場合 The Ultimate Dodge Modを使用している場合か、 アニメーション管理ツールのNemesis実行時に「The Ultimate Dodge Mod」にチェックを入れて処理した場合、人間NPCのアニメーションから受信できるAnimationEventが増える。 通常攻撃時は"NextAttackInitiate"、パワーアタック時は"NextPowerAttackInitiate"が発生する。 (スプリントのパワーアタック時も"NextPowerAttackInitiate"が発生する) #highlight(linenumber,php){{ Event OnAnimationEvent(ObjectReference akSource, string asEventName) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isPowerAtk = false; ;------------------------------------------------------- ; Check Power Attack ;------------------------------------------------------- If (asEventName == "NextAttackInitiate") isPowerAtk = false ElseIf (asEventName == "NextPowerAttackInitiate") isPowerAtk = true Endif; (asEventName == "NextAttackInitiate") ElseIf ... EndEvent; OnAnimationEvent() }} **設備や器具の判定 設備や器具であるかを判定する為の処理例。 基本的には、キーワードで判定することが可能。 椅子などは、キーワードで判定できない場合もあるので注意。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name isSittingObject ; @desc 対象のオブジェクトが座るタイプの家具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 座るタイプの家具の場合はtrue ; ---------------------------------------------------------- Bool Function isSittingObject(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; IsTable [KYWD:0009B9A9] ; isBarStool [KYWD:00074EC7] If frmTarget.HasKeywordString("IsTable") \ || frmTarget.HasKeywordString("isBarStool") isObject = true; Endif; frmTarget.HasKeywordString("IsTable") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSittingObject() ; ---------------------------------------------------------- ; @name isSleepingObject ; @desc 対象のオブジェクトが寝るタイプの家具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 寝るタイプの家具の場合はtrue ; ---------------------------------------------------------- Bool Function isSleepingObject(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; FurnitureBedRoll [KYWD:000E4AD6] If frmTarget.HasKeywordString("FurnitureBedRoll") isObject = true; Endif; frmTarget.HasKeywordString("FurnitureBedRoll") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSleepingObject() ; ---------------------------------------------------------- ; @name isAlchemyLab ; @desc 対象のオブジェクトが錬金器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 錬金器具の場合はtrue ; ---------------------------------------------------------- Bool Function isAlchemyLab(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isAlchemy [KYWD:0002A40B] ; WICraftingAlchemy [KYWD:0004F6E6] If frmTarget.HasKeywordString("isAlchemy") \ || frmTarget.HasKeywordString("WICraftingAlchemy") isObject = true; Endif; frmTarget.HasKeywordString("isAlchemy") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isAlchemyLab() ; ---------------------------------------------------------- ; @name isCookingObject ; @desc 対象のオブジェクトが調理器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 調理器具の場合はtrue ; ---------------------------------------------------------- Bool Function isCookingObject(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isCookingSpit [KYWD:00068ADA] ; CraftingCookpot [KYWD:000A5CB3] ; isSmallCookingPot [KYWD:001010B2] ; isSmallCookingPotDBPoison [KYWD:001010B5] ; isCraftingOven [KYWD:01002840] If frmTarget.HasKeywordString("isCookingSpit") \ || frmTarget.HasKeywordString("isCraftingOven") \ || frmTarget.HasKeywordString("CraftingCookpot") \ || frmTarget.HasKeywordString("isSmallCookingPot") \ || frmTarget.HasKeywordString("isSmallCookingPotDBPoison") isObject = true; Endif; frmTarget.HasKeywordString("isCookingSpit") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isCookingObject() ; ---------------------------------------------------------- ; @name isEnchanter ; @desc 対象のオブジェクトが通常の付呪器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 通常の付呪器具の場合はtrue ; ---------------------------------------------------------- Bool Function isEnchanter(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isEnchanting [KYWD:0006E2A3] ; WICraftingEnchanting [KYWD:0004F6DD] ; DLC2StaffEnchanter [KYWD:02017738] If frmTarget.HasKeywordString("DLC2StaffEnchanter") isObject = false; Elseif frmTarget.HasKeywordString("isEnchanting") \ || frmTarget.HasKeywordString("WICraftingEnchanting") isObject = true; Endif; frmTarget.HasKeywordString("DLC2StaffEnchanter") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isEnchanter() ; ---------------------------------------------------------- ; @name isStaffEnchanter ; @desc 対象のオブジェクトが杖の付呪器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 杖の付呪器具の場合はtrue ; ---------------------------------------------------------- Bool Function isStaffEnchanter(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; DLC2StaffEnchanter [KYWD:02017738] If frmTarget.HasKeywordString("DLC2StaffEnchanter") isObject = true; Endif; frmTarget.HasKeywordString("DLC2StaffEnchanter") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isStaffEnchanter() ; ---------------------------------------------------------- ; @name isSharpeningWheel ; @desc 対象のオブジェクトが研ぎ石かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 研ぎ石の場合はtrue ; ---------------------------------------------------------- Bool Function isSharpeningWheel(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; WICraftingSmithingTempering [KYWD:0004F6FD] ; CraftingSmithingSharpeningWheel [KYWD:00088108] If frmTarget.HasKeywordString("WICraftingSmithingTempering") \ || frmTarget.HasKeywordString("CraftingSmithingSharpeningWheel") isObject = true; Endif; frmTarget.HasKeywordString("WICraftingSmithingTempering") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSharpeningWheel() ; ---------------------------------------------------------- ; @name isArmorTable ; @desc 対象のオブジェクトが作業机かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 作業机の場合はtrue ; ---------------------------------------------------------- Bool Function isArmorTable(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isBlacksmithWorkbench [KYWD:000D932E] ; CraftingSmithingArmorTable [KYWD:000ADB78] If frmTarget.HasKeywordString("isBlacksmithWorkbench") \ || frmTarget.HasKeywordString("CraftingSmithingArmorTable") isObject = true; Endif; frmTarget.HasKeywordString("isBlacksmithWorkbench") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isArmorTable() ; ---------------------------------------------------------- ; @name isForge ; @desc 対象のオブジェクトが鍛造器具かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 鍛造器具の場合はtrue ; ---------------------------------------------------------- Bool Function isForge(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isBlacksmithForge [KYWD:000CAE0A] ; CraftingSmithingForge [KYWD:00088105] ; isBlacksmithAnvil [KYWD:000EB60B] If frmTarget.HasKeywordString("isBlacksmithForge") \ || frmTarget.HasKeywordString("isBlacksmithAnvil") \ || frmTarget.HasKeywordString("CraftingSmithingForge") isObject = true; Endif; frmTarget.HasKeywordString("isBlacksmithForge") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isForge() ; ---------------------------------------------------------- ; @name isTanningRack ; @desc 対象のオブジェクトが皮なめしかどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 皮なめしの場合はtrue ; ---------------------------------------------------------- Bool Function isTanningRack(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isTanning [KYWD:000727A0] ; CraftingTanningRack [KYWD:0007866A] If frmTarget.HasKeywordString("isTanning") \ || frmTarget.HasKeywordString("CraftingTanningRack") isObject = true; Endif; frmTarget.HasKeywordString("isTanning") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isTanningRack() ; ---------------------------------------------------------- ; @name isSmelter ; @desc 対象のオブジェクトが溶鉱炉かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 溶鉱炉の場合はtrue ; ---------------------------------------------------------- Bool Function isSmelter(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isSmelter [KYWD:0009C6C3] ; CraftingSmelter [KYWD:000A5CCE] If frmTarget.HasKeywordString("isSmelter") \ || frmTarget.HasKeywordString("CraftingSmelter") isObject = true; Endif; frmTarget.HasKeywordString("isSmelter") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isSmelter() ; ---------------------------------------------------------- ; @name isWoodChoppingBlock ; @desc 対象のオブジェクトが薪割りかどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 薪割りの場合はtrue ; ---------------------------------------------------------- Bool Function isWoodChoppingBlock(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; FurnitureWoodChoppingBlock / 0x00072dfb (KYWD) If frmTarget.HasKeywordString("FurnitureWoodChoppingBlock") isObject = true; Endif; frmTarget.HasKeywordString("FurnitureWoodChoppingBlock") ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isWoodChoppingBlock() ; ---------------------------------------------------------- ; @name isOreVein ; @desc 対象のオブジェクトが鉱脈かどうか ; @function ; @global ; @param obrfTarget {ObjectReference} 対象のオブジェクト ; @returns {Bool} ; 鉱脈の場合はtrue ; ---------------------------------------------------------- Bool Function isOreVein(ObjectReference obrfTarget) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isObject = false; Form frmTarget = obrfTarget.GetBaseObject(); ;------------------------------------------------------- ; Check the name in the name list ;------------------------------------------------------- ; isPickaxeFloor [KYWD:000613A8] ; isPickaxeTable [KYWD:000613A9] ; isPickaxeWall [KYWD:000A82C3] If frmTarget.HasKeywordString("isPickaxeFloor") \ || frmTarget.HasKeywordString("isPickaxeTable") \ || frmTarget.HasKeywordString("isPickaxeWall") isObject = true; Endif; frmTarget.HasKeywordString("isPickaxeFloor") || ... ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isObject; EndFunction; isOreVein() }} **盗み判定 盗みかどうかを判定する関数はない為、不法侵入と所有権で判定している。 #highlight(linenumber,php){{ Bool Function isStealing(ObjectReference obrfTarget, Actor actTarget) ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Bool isStealing; ActorBase abTarget; Cell celThis; ActorBase abOwnThis; Faction fctOwnThis; ActorBase abOwnHere; Faction fctOwnHere; ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- isStealing = actTarget.IsTrespassing(); abTarget = actTarget.GetActorBase(); celThis = obrfTarget.GetParentCell(); abOwnThis = obrfTarget.GetActorOwner(); fctOwnThis = obrfTarget.GetFactionOwner(); abOwnHere = celThis.GetActorOwner(); fctOwnHere = celThis.GetFactionOwner(); ;------------------------------------------------------- ; Check Stealing ;------------------------------------------------------- If abOwnThis && (abOwnThis != abTarget) isStealing = true; Elseif abOwnHere && (abOwnHere != abTarget) isStealing = true; Elseif fctOwnThis && !actTarget.IsInFaction(fctOwnThis) isStealing = true; Elseif fctOwnHere && !actTarget.IsInFaction(fctOwnHere) isStealing = true; Endif; abOwnThis && (abOwnThis != abTarget) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; isStealing() }} *配置オブジェクトの操作 セル内に配置されたオブジェクトを操作する処理。 **配置オブジェクトを一括で初期位置に戻す MoveToMyEditorLocation()が初期位置に戻す処理。 この例では、nObjectTypeでオブジェクトの種類を指定して、セル内のオブジェクトを一括で元の位置に戻している。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name resetPositionsByObjectTypeIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [nObjectType=0] {Int} ; @returns {Int} ; ---------------------------------------------------------- Int Function resetPositionsByObjectTypeIn( \ Cell celTarget = None, \ Int nObjectType = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ObjectReference obrfEach; Int nIdx; Int nCnt; Int nReset = 0; ;------------------------------------------------------- ; Check the target cell ;------------------------------------------------------- If !celTarget celTarget = Game.GetPlayer().GetParentCell(); Endif; !celTarget ;------------------------------------------------------- ; Loop of objects ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nObjectType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nObjectType); If obrfEach obrfEach.MoveToMyEditorLocation(); nReset += 1; Endif; obrfEach nIdx += 1; EndWhile; (nIdx < nCnt) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nReset; EndFunction; resetPositionsByObjectTypeIn() }} **配置オブジェクトを一括で固定化する SetMotionType()でMotion_Keyframedを指定すると、オブジェクトが固定化されて動かなくなる。 つまり、体当たりしても動かないオブジェクトになる。 この例では、nObjectTypeでオブジェクトの種類を指定して、セル内のオブジェクトを一括で固定化したり、固定化を解除したりしている。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name setMotionTypeByObjectTypeIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [enableLock=true] {Bool} ; @param [nObjectType=0] {Int} ; @returns {Int} ; ---------------------------------------------------------- Int Function setMotionTypeByObjectTypeIn( \ Cell celTarget = None, \ Bool enableLock = true, \ Int nObjectType = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ObjectReference obrfEach; Int nIdx; Int nCnt; Int nLockCnt = 0; ;------------------------------------------------------- ; Check the target cell ;------------------------------------------------------- If !celTarget celTarget = Game.GetPlayer().GetParentCell(); Endif ;------------------------------------------------------- ; Loop of objects ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nObjectType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nObjectType); ; Motion_Dynamic = 1 ; Motion_SphereIntertia = 2 ; Motion_BoxIntertia = 3 ; Motion_Keyframed = 4 ; Motion_Fixed = 5 ; Motion_ThinBoxIntertia = 6 ; Motion_Character = 7 If obrfEach.GetMass() && enableLock obrfEach.SetMotionType(obrfEach.Motion_Keyframed); nLockCnt += 1; Elseif !enableLock obrfEach.SetMotionType(obrfEach.Motion_Dynamic); nLockCnt += 1; Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return nLockCnt; EndFunction; setMotionTypeByObjectTypeIn() }} **配置オブジェクトを一括で収集する 死体は単純にMoveTo()では移動できないので、Disable()してMoveTo()してEnable()している。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name gatherObjectsIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [obrfMoveTo=None] {ObjectReference} ; @param [nType=0] {Int} ; Form-Type ; @param [nIsDeadAlive=0] {Int} ; 0 = both, 1 = lives only, -1 = deads only ; @returns {Bool} ; ---------------------------------------------------------- Bool Function gatherObjectsIn( \ Cell celTarget = None, \ ObjectReference obrfMoveTo = None, \ Int nType = 0, \ Int nIsDeadAlive = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nIdx; Int nCnt; ObjectReference obrfEach; Actor actEach; ;------------------------------------------------------- ; Check arguments ;------------------------------------------------------- If !obrfMoveTo obrfMoveTo = Game.GetPlayer() as ObjectReference; Endif If !celTarget celTarget = obrfMoveTo.GetParentCell(); Endif ;------------------------------------------------------- ; Scan chests ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nType); If !obrfEach.IsEnabled() || obrfEach.IsDeleted() obrfEach = None; Endif If obrfEach && (nType == 43) actEach = obrfEach as Actor; Else actEach = None; Endif If actEach If (nIsDeadAlive > 0) && actEach.IsDead() obrfEach = None; actEach = None; Elseif (nIsDeadAlive < 0) && !actEach.IsDead() obrfEach = None; actEach = None; Endif Endif If actEach && (nType == 43) If actEach.IsDead() actEach.Disable(); Endif; actEach.IsDead() actEach.MoveTo(obrfMoveTo); If actEach.IsDead() actEach.Enable(); Endif; actEach.IsDead() Elseif !actEach && obrfEach obrfEach.MoveTo(obrfMoveTo); Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; gatherObjectsIn() }} **配置オブジェクトを一括で検知する 変性呪文の検知魔法のようなもの。スクリプトで実装すれば、好きなエフェクトを設定できる。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name detectObjectsIn ; @function ; @global ; @param [celTarget=None] {Cell} ; @param [nType=0] {Int} ; Form-Type ; @param [isLootableOnly=false] {Bool} ; @param [nIsDeadAlive=0] {Int} ; 0 = both, 1 = lives only, -1 = deads only ; @returns {Bool} ; ---------------------------------------------------------- Bool Function detectObjectsIn( \ Cell celTarget = None, \ Int nType = 0, \ Bool isLootableOnly = false, \ Int nIsDeadAlive = 0 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Int nIdx; Int nCnt; ObjectReference obrfEach; Actor actEach; Actor actPlayer = Game.GetPlayer(); EffectShader efsEnemy; EffectShader efsAlly; Hazard hzdDetect; Bool isLootable; ;------------------------------------------------------- ; Get Effect ;------------------------------------------------------- ; LifeDetected [EFSH:0x000146] ; LifeDetectedEnemy [EFSH:0x0DC209] ; LifeDetectedUndead [EFSH:0x0AAEB3] ; LifeDetectedUndeadEnemy [EFSH:0x016439] ; KynesPeaceFXS [EFSH:0x084B39] ; HealCircleFXS [EFSH:0x10CDC9] ; WerewolfTransFXS [EFSH:0x0EBEC5] ; WerewolfTrans02FXS [EFSH:0x0EBECD] ; DragonPowerAbsorbFXS [EFSH:0x0280C0] ; AbsorbBlueFXS [EFSH:0x0ABF08] ; AbsorbGreenFXS [EFSH:0x0ABF07] ; AbsorbHealthFXS [EFSH:0x0ABEFF] efsAlly = Game.GetForm(0x000146) as EffectShader; efsEnemy = Game.GetForm(0x0DC209) as EffectShader; ; CircleOfProtectionHazard [HAZD:0x04E80C] ; CircleVitalityHazard [HAZD:0x0B62EA] ; GuardianCircleHazard [HAZD:0x0E0CD3] ; GuardianCircleTurnHazard [HAZD:0x0FEAD3] hzdDetect = Game.GetForm(0x04E80C) as Hazard; ;------------------------------------------------------- ; Check the target cell ;------------------------------------------------------- If !celTarget celTarget = Game.GetPlayer().GetParentCell(); Endif ;------------------------------------------------------- ; Scan chests ;------------------------------------------------------- nIdx = 0; nCnt = celTarget.GetNumRefs(nType); While (nIdx < nCnt) obrfEach = celTarget.GetNthRef(nIdx, nType); If !obrfEach.IsEnabled() || obrfEach.IsDeleted() obrfEach = None; Endif If obrfEach && (nType == 43) actEach = obrfEach as Actor; Else actEach = None; Endif If actEach If (nIsDeadAlive > 0) && actEach.IsDead() obrfEach = None; actEach = None; Elseif (nIsDeadAlive < 0) && !actEach.IsDead() obrfEach = None; actEach = None; Endif Endif If !isLootableOnly isLootable = false; Elseif actEach isLootable = (actEach.GetNumItems() > 1); Elseif obrfEach && (nType == 28) isLootable = (obrfEach.GetNumItems() > 0); Else isLootable = false; Endif If isLootableOnly && !isLootable obrfEach = None; actEach = None; Endif If actEach && (nType == 43) If isEnemy(actEach) efsEnemy.Play(actEach, 26.0); Else efsAlly.Play(actEach, 26.0); Endif; isEnemy(actEach) Elseif !actEach && obrfEach obrfEach.placeAtMe(hzdDetect); Endif nIdx += 1; EndWhile ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; detectObjectsIn() }} *NPCを操作する NPCをPCのように操作する為に必要な一連の処理。 PCの操作停止、NPCの操作、カメラの切替、など。 #highlight(linenumber,php){{ Bool Function startNpcControl(Actor actTarget) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ;------------------------------------------------------- ; Start controlling ;------------------------------------------------------- Game.DisablePlayerControls( \ abMovement = false, \ abFighting = true, \ abCamSwitch = false, \ abLooking = false, \ abSneaking = false, \ abMenu = true, \ abActivate = false, \ abJournalTabs = false, \ aiDisablePOVType = 0 ); actTarget.SetPlayerControls(true); Game.SetPlayerAIDriven(true); actTarget.EnableAI(); Game.SetCameraTarget(actTarget); Game.ForceThirdPerson(); ;------------------------------------------------------- ; Return ;------------------------------------------------------- Return true; EndFunction; startNpcControl() Bool Function stopNpcControl(Actor actTarget) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Switch the controlling ;------------------------------------------------------- actTarget.SetPlayerControls(false); Game.SetPlayerAIDriven(false); Game.EnablePlayerControls(); actTarget.EnableAI(); Game.SetCameraTarget(actPlayer); ;------------------------------------------------------- ; Return ;------------------------------------------------------- Return true; EndFunction; stopNpcControl() }} *マップ・マーカーを表示する 指定したクエストのエイリアスに任意のリファレンスをマーカーとして表示する。 クエストは名前でも指定可能。エイリアスは名前やIDや番号でも指定可能。 例では、isActiveで表示と非表示を切り替える。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name showMapMarker ; @function ; @global ; @param obrfTarget {ObjectReference} ; @param [isActive=true] {Bool} ; @param [szQuestName=] {String} ; @param [nObjective=1] {Int} ; @param [szAliasName=RefAlias001] {String} ; @param [qstMarker=None] {Quest} ; @param [ralsMarker=None] {ReferenceAlias} ; @param [nAliasIdx=-1] {Int} ; @param [nAliasID=-1] {Int} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function showMapMarker( \ ObjectReference obrfTarget, \ Bool isActive = true, \ String szQuestName = "", \ Int nObjective = 1, \ String szAliasName = "RefAlias001", \ Quest qstMarker = None, \ ReferenceAlias ralsMarker = None, \ Int nAliasIdx = -1, \ Int nAliasID = -1 \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Bool isOK = false; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !obrfTarget return isOK; Endif ;------------------------------------------------------- ; Get Marker Quest ;------------------------------------------------------- If !qstMarker qstMarker = Quest.GetQuest(szQuestName); Endif If !qstMarker return isOK; Endif ;------------------------------------------------------- ; Get Reference Alias ;------------------------------------------------------- If !ralsMarker && (nAliasIdx >= 0) ralsMarker = \ qstMarker.GetNthAlias( \ nAliasIdx ) as ReferenceAlias; Elseif !ralsMarker && (nAliasID >= 0) ralsMarker = \ qstMarker.GetAliasById( \ nAliasID ) as ReferenceAlias; Elseif !ralsMarker && (szAliasName != "") ralsMarker = \ qstMarker.GetAliasByName( \ szAliasName ) as ReferenceAlias; Endif If !ralsMarker return isOK; Endif ;------------------------------------------------------- ; Show Marker ;------------------------------------------------------- If isActive ralsMarker.ForceRefTo(obrfTarget); qstMarker.SetObjectiveDisplayed( \ nObjective, true, false ); isOK = true; Else ralsMarker.Clear(); qstMarker.SetObjectiveDisplayed( \ nObjective, false, false ); isOK = true; Endif ;------------------------------------------------------- ; Return ;------------------------------------------------------- return isOK; EndFunction; showMapMarker() }} *対象の人の戦闘を強制停止させる 戦闘を停止させる処理の詰め合わせ。 StopCombat()は対象のアクターの戦闘状態を停止させることしか出来ない。 本格的に戦闘停止させるには、警戒状態も解除し、相手方の戦闘状態も解除する必要がある。 disableRecoverのオプションは、戦闘停止時に全回復するようなMOD対策。 戦闘停止後に、停止前の体力に強制的に戻す為のオプション。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name stopCombatEx ; @function ; @global ; @param actTarget {Actor} ; @param [disableRecover=true] {Bool} ; @param [enableStopAlarm=false] {Bool} ; @param [enableStopTarget=false] {Bool} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function stopCombatEx( \ Actor actTarget, \ Bool disableRecover = true, \ Bool enableStopAlarm = false, \ Bool enableStopTarget = false \ ) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- Float nHealth; Float nDamage; Actor actEnemy; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !actTarget || !actTarget.IsInCombat() return false; Endif; !actTarget || !actTarget.IsInCombat() ;------------------------------------------------------- ; Get Current Health ;------------------------------------------------------- If disableRecover nHealth = actTarget.GetActorValue("Health"); Endif; disableRecover ;------------------------------------------------------- ; Stop Combat (Self) ;------------------------------------------------------- actTarget.StopCombat(); ;------------------------------------------------------- ; Stop Alarm ;------------------------------------------------------- If enableStopAlarm actTarget.StopCombatAlarm(); Endif; enableStopAlarm ;------------------------------------------------------- ; Stop Combat (Enemy) ;------------------------------------------------------- If enableStopTarget actEnemy = actTarget.GetCombatTarget(); If actEnemy stopCombatEx(actEnemy, disableRecover, false, false); Endif Endif; enableStopTarget ;------------------------------------------------------- ; Damage Health ;------------------------------------------------------- If disableRecover nDamage = actTarget.GetActorValue("Health") - nHealth; If (nDamage > 0.0) actTarget.DamageActorValue("Health", nDamage); Endif; (nDamage > 0.0) Endif; disableRecover ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; stopCombatEx() }} *外見変更 **顔テキスチャの変更 NetImmerse Overrideが必要。 アクターの顔テキスチャを変更する処理。顔用のTextureSetを指定する。 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name changeFace ; @function ; @global ; @param actTarget {Actor} ; @param txtSkin {TextureSet} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function changeFace(Actor actTarget, TextureSet txtSkin) global ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget; Int nHeadIdx = -1; String szNode = ""; ;------------------------------------------------------- ; Check Arguments ;------------------------------------------------------- If !actTarget || !txtSkin return false; Else abTarget = actTarget.GetLeveledActorBase(); Endif; !actTarget || !txtSkin ;------------------------------------------------------- ; Get Head Index ;------------------------------------------------------- If abTarget nHeadIdx = abTarget.GetIndexOfHeadPartByType(1); Endif; abTarget ;------------------------------------------------------- ; Get Node Name ;------------------------------------------------------- If (nHeadIdx >= 0) szNode = abTarget.GetNthHeadPart(nHeadIdx).GetName(); Endif; (nHeadIdx >= 0) ;------------------------------------------------------- ; Apply Face Texture ;------------------------------------------------------- If (nHeadIdx >= 0) abTarget.SetFaceTextureSet(txtSkin); NetImmerse.SetNodeTextureSet( \ actTarget, szNode, txtSkin, false); Endif; (nHeadIdx >= 0) ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; changeFace() }} **髪パーツの変更 #highlight(linenumber,php){{ ; ---------------------------------------------------------- ; @name changeHair ; @function ; @global ; @param actTarget {Actor} ; @param hpNewHair {HeadPart} ; @returns {Bool} ; ---------------------------------------------------------- Bool Function changeHair(Actor actTarget, HeadPart hpNewHair) ;------------------------------------------------------- ; Init Variables ;------------------------------------------------------- ActorBase abTarget = actTarget.GetLeveledActorBase(); Bool isPlayer = (Game.GetPlayer() == actTarget); Int nHeadIdx; HeadPart hpCurHair; ;------------------------------------------------------- ; Get Hair Headparts ;------------------------------------------------------- nHeadIdx = abTarget.GetIndexOfHeadPartByType(3); hpCurHair = abTarget.GetNthHeadPart(nHeadIdx); ;------------------------------------------------------- ; Reset Hair Part ;------------------------------------------------------- If !isPlayer && hpNewHair actTarget.ReplaceHeadPart(hpCurHair, hpNewHair); Elseif isPlayer && hpNewHair actTarget.ChangeHeadPart(hpNewHair); Endif; !isPlayer && hpNewHair ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; changeHair() }} *プレイヤーのヘッド・トラッキング 指定したオブジェクトに対して、プレイヤーにヘッド・トラッキングさせる処理。 自分自身を対象に指定すると、ゆっくり正面を向く。(ヘッド・トラッキングは継続) ヘッド・トラッキングそのものを停止すると、急に正面を向く。 #highlight(linenumber,php){{ Bool Function startPlayerHeadTracking(ObjectReference obrfTarget) ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Player Head Tracking ;------------------------------------------------------- If obrfTarget actPlayer.SetHeadTracking(true); actPlayer.SetLookAt(obrfTarget, false); actPlayer.SetAnimationVariableBool("bHeadTrackSpine", false); actPlayer.SetAnimationVariableInt("IsNPC", 1); Endif; obrfTarget ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; startPlayerHeadTracking() Bool Function stopPlayerLooking() ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Player Head Tracking ;------------------------------------------------------- actPlayer.SetLookAt(actPlayer, false); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; stopPlayerLooking() Bool Function stopPlayerHeadTracking() ;------------------------------------------------------- ; Declare Variables ;------------------------------------------------------- Actor actPlayer = Game.GetPlayer(); ;------------------------------------------------------- ; Player Head Tracking ;------------------------------------------------------- actPlayer.ClearLookAt(); actPlayer.SetAnimationVariableBool( \ "bHeadTrackSpine", false ); actPlayer.SetAnimationVariableInt("IsNPC", 0); actPlayer.SetHeadTracking(false); ;------------------------------------------------------- ; Return ;------------------------------------------------------- return true; EndFunction; stopPlayerHeadTracking() }} *配列の要素数を128を超えたものを扱いたい、または動的に配列を宣言したい Papyrusの配列は通常は1~128までの範囲しか扱う事ができず、 それ以上の配列数を宣言しようと128を超える数値を指定するとエラーが発生します。 また、配列宣言時に配列数の部分を変数で指定した場合もエラーが発生します。 **NGパターン #highlight(linenumber,php){{ Actor[] NPCArray int[] ValArray Function Test() int num = 0 NPCArray = new Actor[200] ;1~128までの範囲で宣言しろとエラーが出る ; 変数の値分の配列を宣言したいが ; 配列数の指定に変数を指定するなとエラーが出る num = 200 ValArray = new int[num] EndFunction; Test() }} 配列数が128を超える配列を作成したい場合、 Float、Int、String、Form、Alias型の場合は SKSEのPapyrus拡張関数である Utility.Create~Arrayを 用いる事で配列数が128を超える配列を作成することができます。(~の部分は型によって異なりますのでスクリプトリファレンスを確認してください。) https://www.creationkit.com/index.php?title=Utility_Script Actor型の場合は標準のSKSEの拡張関数には存在しないため Papyrus拡張SKSEプラグインであるPapyrusUtilが必要となります。 この方法で配列の宣言をする場合、処理に必要な配列数を動的に宣言できるようになる他に第2引数に値を指定する事で宣言と同時に各要素の値を指定値で初期化する事ができます。 **実装例 #highlight(linenumber,php){{ Actor[] NPCArray int[] ValArray Function Test() int num = 200 ; NPCArray = PapyrusUtil.ActorArray(num, None) ValArray = Utility.CreateIntArray(num, 10) ; 配列の各要素の値を10で初期化 EndFunction; Test() }} *装備してる武器や防具に付いているエンチャントを取得する(自分で付呪したものも取得する方法) 装備してる武器、防具のエンチャントを取得する処理。 WeaponとArmorのGetEnchantment関数は自分から付呪を施したエンチャントを取得できないため、 WornObject.GetEnchantment関数でのチェックも行う。 #highlight(linenumber,php){{ ; 装備してる武器のエンチャントを取得 ; iRightHandに0を渡せば左手武器のエンチャントを取得 Enchantment Function GetWeaponEnchantment(Actor akTarget, int iRightHand = 1) Weapon w = akTarget.GetEquippedObject(iRightHand) as Weapon if !w return None endif Enchantment ench = w.GetEnchantment() if ench return ench endif return WornObject.GetEnchantment(akTarget, iRightHand, 0) EndFunction ; 装備してる防具のエンチャントを取得 Enchantment Function GetArmorEnchantment(Actor akTarget, int iSlotMask) Armor arm = akTarget.GetWornForm(iSlotMask) as Armor if !arm return None endif Enchantment ench = arm.GetEnchantment() if ench return ench endif return WornObject.GetEnchantment(akTarget, 0, iSlotMask) EndFunction }} 上記サンプルのGetArmorEnchantment関数で第ニ引数に渡す値はスロットマスクとなります。 スロットマスクの値は[[こちら>https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor]]のページを参照してください。 最初からエンチャントが付いてる装備については上記のようにGetEquippedObjectやGetWornForm等でアイテムを取得し それに対してGetEnchantment関数を使用すれば取得可能です。 ただし、GetEquippedObject等で取得できる武器と防具のデータはベースフォームであり、 自分から付呪を行った武器や防具に対してGetEnchantment関数を使用した場合はNoneを返します。 自分から付呪を行った武器や防具のエンチャントを取得したい場合は WornObjectオブジェクトのGetEnchantment関数を使用すれば取得できます。 ただし、WornObjectオブジェクトのGetEnchantment関数は &color(#F54738){対象の装備に自分で付呪したエンチャントがある場合のみ、エンチャントを返す}という仕様のため 逆に最初からエンチャントが付いてる装備の場合はNoneを返します。 そのため、現在装備しているアイテムのエンチャントを取得する場合は サンプルのように最初から付呪がされてるアイテムであるか確認し、 そうでない場合、WornObjectオブジェクトのGetEnchantment関数で自前で作成した付呪を取得するようにしましょう。 *MCMの設定を保存、ゲーム開始時や再開時に設定を自動読込する 外部ファイルにMCMの設定を保存する事でニューゲーム時や違うセーブデータでゲームを再開した時に 一々MCMの再設定をする手間を省かせるようにする。 現状でメジャーなのは[[PapyrusUtil>https://skyrimspecialedition.2game.info/detail.php?id=13048]]のJsonUtilオブジェクトを使用する方法か [[MCMHelper(SEのみ)>https://skyrimspecialedition.2game.info/detail.php?id=53000]]を使用する方法だが、 PapyrusUtilによる方法を記載する。 &color(#F54738){注:FISSでの自動読込は禁止。仕組み上の問題で複数のスクリプトが同時にFISSを使用するとCTDとなるため。} #highlight(linenumber,php){{ Scriptname [スクリプト名] extends ski_configbase GlobalVariable Property SampleGV auto ~省略~ String settings_path = "..\\[Mod名]\\UserSettings" ; MCMはセーブからのロード時にOnGameReloadイベントが発生するため ; この時に設定の読み込みを行う Event OnGameReload() parent.OnGameReload() LoadUserSettingsPapyrus() endEvent ; MCMを閉じた時に実行 ; 設定を自動保存 Event OnConfigClose() parent.OnConfigClose() SaveUserSettingsPapyrus() EndEvent ; MCMの初期化 ; ニューゲーム時に設定の自動読込が実行される event OnConfigInit() ;MCMの初期設定を記述 ~省略~ LoadUserSettingsPapyrus() endEvent Bool function SaveUserSettingsPapyrus() JsonUtil.SetPathIntValue(settings_path, "Sample", SampleGV.GetValueInt()) if !JsonUtil.Save(settings_path, false) debug.Trace(ModName + ": Error saving user settings.", 0) return false endIf return true endFunction Bool function LoadUserSettingsPapyrus() if !JsonUtil.IsGood(settings_path) ShowMessage(ModName + ": Can't load User Settings. Errors: {" + JsonUtil.getErrors(settings_path) + "}") return false endIf SampleGV.SetValueInt(JsonUtil.GetPathIntValue(settings_path, "Sample", SampleGV.GetValueInt())) return true endFunction ~OnConfigOpen等のMCMの各イベントの処理の記述は省略~ }} MCMを開いて後に閉じた後に (ルートディレクトリ)\SKSE\Plugins\[Mod名]ディレクトリにUserSettings.jsonが生成されている事、 セーブからのロードやニューゲーム時にMCMの設定が引き継がれている事を確認できたら、 MCMの自動保存と自動読込の作成は完了となります。 (MO2の場合は\overwrite\SKSE\Plugins\[Mod名]ディレクトリにUserSettings.jsonが生成されます。) ・上記サンプルでUserSettings.jsonに記載される内容 #highlight(linenumber,php){{ { Sample:[設定値(整数)] } }} ゲーム開始時にModの設定を一つ一つ手動でロードするのはユーザー側にとってかなり手間となるため 基本的には自動読込は実装するようにしましょう。 保存に関しては設定の切り替えが簡単だったり項目数が少なければ自動保存で問題ありませんが、 設定項目がかなりの数がある場合は自動保存はMCM側の設定で実行するか制御できるようにし、 手動保存と手動読込の手段も一緒に追加すると良いです。 余裕があれば自動保存、自動読込は設定で制御できるようにすると良いでしょう。

表示オプション

横に並べて表示:
変化行の前後のみ表示:
目安箱バナー