逆引きリファレンス
この項目ではやりたいことから調べられます。
やり方は一例であって必ずしもということはないので、他のパターンもあればぜひ記載してください。


ゲーム開始時に魔法やアイテムを自動で追加したい(初期化クエスト)


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で当クエストの指定も忘れずに。


常時稼動させるスクリプト

※できる限り使わないことを考えぬいてください。
常時稼働する事自体スクリプト(Papyrus)かそうでないかにかかわらず負荷がかかるために避けたほうが良いです。
とくに 間隔の短い 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())

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が自身に保存されたプロパティ情報を元に初期化しようとしたがスクリプトには存在しないために出る注意文で、新規ゲームでも関係なくメッセージを出します。
実害は無いですが邪魔な存在である事は確かなのでなるべくなら消しましょう。

  • 消し方
消したいプロパティを選択後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というツールで消せます。


扉の開錠/施錠



扉の施錠判断には ObjectReference.IsLocked を使います。扉の開錠/施錠には ObjectReference.Lock を使います。
以下は、アクティベートイベントを元に、対象の扉が開いている場合は施錠/閉じている場合は解錠するスクリプトの例です。

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 タブにある情報を設定します。
以下、スクリーンショットでは鍵を必要とする状態で扉を閉めています。
何らかのイベント等をトリガーに先に進めるようにする場合などに使えます。


オブジェクトの移動



設置済みのオブジェクトを移動させるには ObjectReference.SetPosition を使います。
以下はアクティベートした対象そのものを後ろへ移動させるスクリプトの例です。

Scriptname ExampleMoveObject extends ObjectReference  

Event OnActivate(ObjectReference akActionRef)
	SetPosition(x, y - 128, z)
endEvent

ObjectReference.SetPosition で指定する座標は、セル内の絶対座標系の値です。
ObjectReference には現在位置を表す x, y, z プロパティがあるので、それを使って相対位置を指定するのが常套手段です。
向きも変えたい場合は ObjectReference.SetAngle を併用します。

※ポイント:この移動方法は、今の座標から徐々に指定の場所へ移動させるものではありません。ObjectReference.SetPosition は、瞬時の移動しかできません。徐々に移動させるには前提として対象が Actor である必要があります。

新しいオブジェクトの設置

簡単な例



オブジェクトを新しく設置するには ObjectReference.PlaceAtMe を使います。
第一引数に Form オブジェクトを指定します。
オブジェクト指向熟練者への補足ですが、以下の対比がちょうど当てはまると覚えておくと良いでしょう。
Form オブジェクト クラス
ObjectReference.PlaceAtMe new 演算子
ObjectReference オブジェクト インスタンス
以下(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 オブジェクトがあれば新しいオブジェクトを生成できます。

スキーヴァーを大量発生させる例



アクティベートするたびにスキーヴァーが部屋のどこかに設置されるサンプルです。

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」が設定してあります。何度もアクティベートすれば当然…。
囲まれます。

FormList を使ったランダム設置の例



アクティベートするたびに FormList の中にある何かが部屋のどこかに設置されるサンプルです。

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

アクターの誘導



アクターとは動きまわるオブジェクトのことです。プレイヤーや敵やNPCなどが該当します。
「オブジェクトの移動」で紹介した ObjectReference.SetPosition は瞬時の移動です。誘導には Actor.PathToReference を使います。
誘導する際、移動先のオブジェクトを指定する必要があります。ObjectReference であれば何でも良いですが、もしも自由に誘導させたいならば 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 をセットし、スキーヴァーの到着を判断して位置を変えていきます。
また、本サンプルは「常時稼動させるスクリプト」のサンプルにもなっています。

倒した敵からコンテナードロップ



敵を倒した際、コンテナーをドロップさせてアイテムをゲットできるようにしてみましょう。
方法は簡単です。Actor.OnDeath イベントで敵が倒されたタイミングを見計らい
  • コンテナーを設置
  • 敵を排除
するだけです。以下、事例です。

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