繰り返しクエストの作り方

Mod作成

今回はデスペナルティModでプレイヤーの死体を回収するクエストを作ります。

Questを用意する

CKで作るのが無難です。

タイプはSide Questならクエスト欄に専用で1つ出てきて、Miscellaneousなら「その他」の欄に他と一緒になって出てきます。

フラグはお好みですが、Start enabledはオンで、最初から起動させておきます。Run onceは付けない方がいいと思います。これを付けるとStopさせたが最後、セーブデータのクリーニングをしないと再起動できなくなるようです。

今回はStageは動かさないのでAllow repeatはどっちでもいいと思います。

Stageは0と10を用意しました。

Reference Aliasを用意する

Reference Aliasは色々なことに使えますが、クエストの目標に使います。

また、死体が勝手に消えないようにする効果もあります。Reference Aliasに入っているObject Reference(Actor)はRespawnの処理の際に消えません。

死体を入れるので、Allow deadはオンです。

今回は3つ用意しました。

死体ができたら、Reference AliasにたいしてForceRefToで死体を代入します。3個までがクエストの目標になります。

Quest Objectiveを作る

クエストの目標そのものになります。Quest Objectiveを作ります。Indexは10にしました。

Reference Aliasを指定します。

Reference Aliasを埋めたあとで、以下のコードでクエストを表示させます。

死体回収クエスト - その1 (papyrus)

; 死体回収クエスト(CKで紐づけておく)
Quest Property CorpseTracker Auto Const

; 死体が生成されたら、この関数でクエストを開始する
; akTargetが死体のReference
Function StartTrackCorpse(Actor akTarget)
    Int i = 0

    ; 用意したReference Aliasが3個なので、空いているところを探す
    while i < 3
        Alias kAlias = CorpseTracker.GetAlias(i)

        if kAlias
            ReferenceAlias kReferenceAlias = kAlias as ReferenceAlias

            if kReferenceAlias
                if !kReferenceAlias.GetReference()
                    kReferenceAlias.ForceRefTo(akTarget)
                    i = 999
                endif
            endif
        endif

        i += 1
    endwhile

    Int iStage = CorpseTracker.GetStage()

    ; 初回はステージが0なので、10に進める
    if iStage == 0
        CorpseTracker.SetStage(10)
    endif

    ; 目標を表示させる
    CorpseTracker.SetObjectiveDisplayed(10, true, true)
EndFunction

SetObjectiveDisplayedはforceをtrueにします。こうしないと、2回目以降に出てきません。

死体にたいする操作

Reference Aliasに以下のコードを紐づけておきます。

死体回収クエスト - その2 (papyrus)

ScriptName AliasCorpseTrackerScript extends ReferenceAlias

; 死体を誰かがアクティベートしたときに呼ばれる
Event OnActivate(ObjectReference akActionRef)
    Actor MySelf = GetActorReference()
    Actor PlayerRef = Game.GetPlayer()

    ; アクティベートしたのがプレイヤーなら距離が空くまで待つ
    ; NPCもアクティベートする場合があるので注意
    if akActionRef == PlayerRef
        RegisterForDistanceGreaterThanEvent(PlayerRef, MySelf, 500.0)
    endif
EndEvent

; 死体とプレイヤーの距離が空いたら呼ばれる
Event OnDistanceGreaterThan(ObjectReference akObj1, ObjectReference akObj2, float afDistance)
    Actor MySelf = GetActorReference()
    Actor PlayerRef = Game.GetPlayer()

    UnregisterForDistanceEvents(PlayerRef, MySelf)
    MySelf.BlockActivation()

    ; 所持品がまだ残っていないか調べる
    Form[] kItems = MySelf.GetInventoryItems()
    Int i = kItems.Length
    Actor:WornItem kPipboy = MySelf.GetWornItem(30)

    while i > 0
        i -= 1

        ; Pipboy以外の何かが見つかったら中断する
        if kItems[i] != kPipboy.Item
            MySelf.BlockActivation(false)
            return
        endif
    endwhile

    ; すべて回収されたようなので、後始末をして終わる
    Clear()
    MySelf.Disable(abFadeOut = true)
    MySelf.Delete()
    RestartQuestStage()
EndEvent

; クエストを終了させる
Function RestartQuestStage()
    Quest kQuest = GetOwningQuest()
    Int i = 0

    ; すべての目標が消えたか確認する
    while i < 3
        Alias kAlias = kQuest.GetAlias(i)

        if kAlias
            ReferenceAlias kReferenceAlias = kAlias as ReferenceAlias

            if kReferenceAlias
                ; まだ目標が残っているので中断する
                if kReferenceAlias.GetReference()
                    return
                endif
            endif
        endif

        i += 1
    endwhile

    ; クエストのステージ10を達成とする
    ; クエストそのものを達成済みにしてしまうと再起動できなくなるみたい
    kQuest.SetObjectiveCompleted(10)
EndFunction

プレイヤーが死体をアクティベートしたら、離れるのを待ちます。

離れたら、所持品を検査して、何も持っていなければ死体を削除します。そしてReference Aliasを空にします。

すべてのReference Aliasが空になったら、クエストの目標を非表示にします。

タイトルとURLをコピーしました