Skyrim MOD作成メモ


WIDeadBodyCleanupScript


このスクリプトはユニークなNPCなどを死体置き場のセル(WIDeadBodyCleanupCell)へ移動させ、
インベントリ内のアイテムを棺などに移動させるスクリプトです。(ついでに自動的に盗品属性が付く)
このスクリプトの使用条件は以下のとおり。
・条件1:ユニークなActorであること(非ユニークなActorの場合、死体消去の処理と競合するからだと思われます)
・条件2:生きている状態で生成されること(死んだ状態で生成されると回収されるためのフラグが立たない)

<設定方法>
ActorのEdit画面左中央にある「Script」のところでAdd⇒WIDeadBodyCleanupScriptを指定
追加されたら選択してPropertiesをクリック
・DaysBeforeCleanup
 ・死体が回収される経過時間。未指定の場合0.5(12時間)
・DeathContainer
 ・死体を収めるコンテナ。Pick Reference in Render Window⇒Render Windowに表示されている棺などを指定
・WI
 ・WIを選択しておく

<スクリプト解説>
Scriptname WIDeadBodyCleanupScript extends Actor  

;**** This should ONLY BE USED ON UNIQUE ACTORS!!! ****

;This script cleans up dead actors, enables a container (ie grave/crypt) and moves all their inventory, after it Loads/unloads after a certain period of time.
;If you need to temporarily stop someone from being cleaned up, put them in the WINoBodyCleanupFaction faction.

;please do not modify this script without talking to jduvall

WIFunctionsScript Property WI Auto
{Pointer to WIFunctionsScript attached to WI quest. You MUST set this or things will be broken.}

float Property DaysBeforeCleanUp = 0.5 Auto
{Default = 0.5: What's the minimum amount of time (in days) that the body should stick around before being cleaned up. (Clean up happens during while loop.)}

ObjectReference Property DeathContainer Auto
{Container to move all the items the actor has in his inventory when cleaned up.}

actor SelfRef     ;used to keep me persistent so I get UnLoad events while I exist

state Dead
    ;do nothing
    Event OnDeath(Actor akKiller)
    EndEvent
    
EndState


Event OnDeath(Actor akKiller)

    GoToState("Dead")
    
    if DeathContainer
        
        bool cleanedUp = false
        
        while cleanedUp == false
;           debug.trace("WIDeadBodyCleanupScript" + self + "OnDeath() In While Loop, will try cleaning up after waiting " + DaysBeforeCleanUp)
            
            utility.waitGameTime(DaysBeforeCleanUp * 24) ;設定された時間経過まで待機(default:0.5 = 12時間)
            
            cleanedUp = checkForCleanup()
            
        endWhile
        
    else
;       debug.trace("WIDeadBodyCleanupScript" + self + " WARNING: NO DeathContainer!", 2)
        
    endif
    
EndEvent

;死体をコンテナに送るかどうかのチェック処理
;WINoBodyCleanupFanctionがついているActorの場合は条件を満たしても送られない(チェック済みにして呼び元のループからは抜ける)
;時間経過をしても死体のあるセルが読み込まれている状態の場合は送られない
bool function checkForCleanup()
    
    if IsInFaction(WI.WINoBodyCleanupFaction)
;       debug.trace("WIDeadBodyCleanupScript" + self + "In Faction WINoBodyCleanupFaction so NOT cleaning up body.", 1)
        ;do nothing
        return true ;bail out of while loop
        
    Elseif Is3DLoaded() == False
;       debug.trace("WIDeadBodyCleanupScript" + self + "Cleaning up body.")
        cleanUpBody()
        return true
    Else
;         debug.trace("WIDeadBodyCleanupScript" + self + "Not cleaning up body, because my 3D is loaded.")
    EndIf

    return false
    
endfunction

;実際に死体をコンテナに送る処理
function cleanUpBody()
;   debug.trace("WIDeadBodyCleanupScript" + self + "cleanUpBody() moving to WIDeadBodyCleanupCellMarker in WIDeadBodyCleanupCell and Calling RemoveAllItems() to DeathContainer, and enabling it:" + DeathContainer)
    
    ;Disable()
    ;*** It has been decided it's safer to move them to a holding cell, for quests that might be filling allowing for dead actors but not allowing checking for disabled actors
    
    MoveTo(WI.WIDeadBodyCleanupCellMarker)       ;死体そのものは専用セルへ送る
    
    DeathContainer.SetActorOwner(GetActorBase()) ;ここで所有権を付けているので「盗む」と表示される
    DeathContainer.Enable()                      ;あらかじめ非表示しておいたオブジェクトの場合、ここで表示がされる
    RemoveAllItems(DeathContainer)
    
EndFunction

神経衰弱スクリプト(bwpMatchCardMasterSCRIPT、bpwMatchCardScript)


このスクリプトは先に引いたカードと後から引いたカードを一致させるゲーム、ようは神経衰弱です。
未実装エリア:OLDBluePalaceWing01に置かれているのですが、これはそのままだと「先に選んだカードが消えない」という問題があり、
もう一度まったく同じカードをactivateすると一致したことになってしまうという穴があってまともなミニゲームになっていません。
スクリプトを改変することでちゃんとしたミニゲームにすることができます。

自作MOD:MorePlacesByYossieにてこのスクリプトをほぼそのまま使っている場所がありますので、
実際の動きを見たい方はそちらで試してみてください。該当の場所はバーレイダーク遺跡になります。

なお、設定は少々面倒です。
カードが10枚あるので、それらすべてに全カードの所在と、どれがどのカードなのかをCreation Kit上で指定してあげる必要があります。

<スクリプト解説>
Scriptname bwpMatchCardMasterSCRIPT extends ObjectReference

;0 = NULL
;1 = Spades
;2 = Hearts
;3 = Diamonds
;4 = Clubs
;5 = Sheo
INT PROPERTY flippedCard=0 AUTO HIDDEN    ;選んだカードがどれなのかのフラグ(番号の内訳は上のコメントの通り)
INT PROPERTY flippedNum=0 AUTO HIDDEN    ;新規追加    選んだカードが1番目なのか2番目なのかのフラグ
Scriptname bpwMatchCardScript extends ObjectReference

;each of the possible cards faces when flipped
OBJECTREFERENCE PROPERTY HeartsA AUTO        ;カードのオブジェクト群 所在を全部CK上で指定する
OBJECTREFERENCE PROPERTY HeartsB AUTO
OBJECTREFERENCE PROPERTY ClubsA AUTO
OBJECTREFERENCE PROPERTY ClubsB AUTO
OBJECTREFERENCE PROPERTY SpadesA AUTO
OBJECTREFERENCE PROPERTY SpadesB AUTO
OBJECTREFERENCE PROPERTY DiamondsA AUTO
OBJECTREFERENCE PROPERTY DiamondsB AUTO
OBJECTREFERENCE PROPERTY SheoA AUTO
OBJECTREFERENCE PROPERTY SheoB AUTO

BOOL PROPERTY HEARTS AUTO        ;カードの種類 CK上で該当のものだけtrueにする
BOOL PROPERTY CLUBS AUTO
BOOL PROPERTY SPADES AUTO
BOOL PROPERTY DIAMONDS AUTO
BOOL PROPERTY SHEO AUTO

BOOL PROPERTY first AUTO    ;新規追加    1番目のカードならtrueにしておく
BOOL PROPERTY second AUTO    ;新規追加    2番目のカードならtrueにしておく

;the script keeping track
OBJECTREFERENCE PROPERTY controllerScript AUTO

;the master script
bwpMatchCardMasterSCRIPT mainScript

;the door
OBJECTREFERENCE PROPERTY portDoor AUTO        ;ミニゲーム成立時にオープンする扉
OBJECTREFERENCE PROPERTY skeevDoor AUTO

EVENT onLoad()
    mainScript = controllerScript as bwpMatchCardMasterSCRIPT    ;ここでロードされたときに初期化しています。つまりロードをはさまないと正しく動きません。
ENDEVENT

EVENT onACTIVATE(objectReference obj)
    
    IF(obj as ACTOR == game.getPlayer())
        
        ; //////////////////////
        ; If it's a Hearts card
        IF(HEARTS)
            ;1枚目を選んだ時の処理
            IF(mainScript.flippedCard == 0)
                mainScript.flippedCard = 2
                IF(first)                     ;処理追加&変更    選んだカードのほうをdisableして選べなくする
                    mainScript.flippedNum = 1
                    HeartsA.disable()
                ELSE
                    mainScript.flippedNum = 2
                    HeartsB.disable()
                ENDIF
                debug.messageBox("Heart")
            ;2枚目を選んで同じ種類だった時の処理
            ELSEIF(mainScript.flippedCard == 2)
                HeartsA.disable()
                HeartsB.disable()
                debug.messageBox("Match")
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
            ;2枚目を選んで違う種類だった時の処理
            ELSE
                EnableBeforeSelectedCard()    ;処理追加    消したカードを元に戻す(サブ関数 詳細は後述)
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                debug.messageBox("Reset")
            
            ENDIF
        
        ; /////////////////////
        ; If it's a clubs card
        ELSEIF(CLUBS)
            
            IF(mainScript.flippedCard == 0)
                mainScript.flippedCard = 4
                IF(first)
                    mainScript.flippedNum = 1
                    ClubsA.disable()
                ELSE
                    mainScript.flippedNum = 2
                    ClubsB.disable()
                ENDIF
                debug.messageBox("Clubs")
                
            ELSEIF(mainScript.flippedCard == 4)
                ClubsA.disable()
                ClubsB.disable()
                debug.messageBox("Match")
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                
            ELSE
                EnableBeforeSelectedCard()
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                debug.messageBox("Reset")
            
            ENDIF
        
        ; //////////////////////
        ; If it's a spades card
        ELSEIF(SPADES)
            
            IF(mainScript.flippedCard == 0)
                mainScript.flippedCard = 1
                IF(first)
                    mainScript.flippedNum = 1
                    SpadesA.disable()
                ELSE
                    mainScript.flippedNum = 2
                    SpadesB.disable()
                ENDIF
                debug.messageBox("Spades")
                
            ELSEIF(mainScript.flippedCard == 1)
                SpadesA.disable()
                SpadesB.disable()
                debug.messageBox("Match")
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                
            ELSE
                EnableBeforeSelectedCard()
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                debug.messageBox("Reset")
            
            ENDIF
        
        ; ////////////////////////
        ; If it's a diamonds card
        ELSEIF(DIAMONDS)
            
            IF(mainScript.flippedCard == 0)
                mainScript.flippedCard = 3
                IF(first)
                    mainScript.flippedNum = 1
                    DiamondsA.disable()
                ELSE
                    mainScript.flippedNum = 2
                    DiamondsB.disable()
                ENDIF
                debug.messageBox("Diamond")
                
            ELSEIF(mainScript.flippedCard == 3)
                DiamondsA.disable()
                DiamondsB.disable()
                debug.messageBox("Match")
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                
            ELSE
                EnableBeforeSelectedCard()
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                debug.messageBox("Reset")
            
            ENDIF
        
        ; ////////////////////
        ; If it's a sheo card
        ELSEIF(SHEO)
            IF(mainScript.flippedCard == 0)
                mainScript.flippedCard = 5
                IF(first)
                    mainScript.flippedNum = 1
                    SheoA.disable()
                ELSE
                    mainScript.flippedNum = 2
                    SheoB.disable()
                ENDIF
                debug.messageBox("Sheo")
                
            ELSEIF(mainScript.flippedCard == 5)
                SheoA.disable()
                SheoB.disable()
                debug.messageBox("Match")
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                
            ELSE
                EnableBeforeSelectedCard()
                mainScript.flippedCard = 0
                mainScript.flippedNum  = 0
                debug.messageBox("Reset")
                
            ENDIF
        
        ELSE
            debug.messageBox("we haven't been set!")
        ENDIF
        
        ;puzzle is complete, open the door
        IF(HeartsA.isEnabled() == FALSE && SpadesA.isEnabled() == FALSE && DiamondsA.isEnabled() == FALSE && ClubsA.isEnabled() == FALSE && SheoA.isEnabled() == FALSE && HeartsB.isEnabled() == FALSE && SpadesB.isEnabled() == FALSE && DiamondsB.isEnabled() == FALSE && ClubsB.isEnabled() == FALSE && SheoB.isEnabled() == FALSE)
            ;debug.messageBox("Open Door!!")
            portDoor.activate(controllerScript)
            skeevDoor.activate(controllerScript)
        ENDIF
    ENDIF
ENDEVENT 

;新規追加のサブ関数
;MASTERscript側に覚えさせた「先に選んで消えたカード」を復活させる処理
;覚えたカードがどれなのか判定してenable()しているだけです。
Function EnableBeforeSelectedCard()
    IF(mainScript.flippedCard == 2)
        IF(mainScript.flippedNum == 1)
            HeartsA.enable()
        ELSE
            HeartsB.enable()
        ENDIF
    ELSEIF(mainScript.flippedCard == 4)
        IF(mainScript.flippedNum == 1)
            ClubsA.enable()
        ELSE
            ClubsB.enable()
        ENDIF
    ELSEIF(mainScript.flippedCard == 1)
        IF(mainScript.flippedNum == 1)
            SpadesA.enable()
        ELSE
            SpadesB.enable()
        ENDIF
    ELSEIF(mainScript.flippedCard == 3)
        IF(mainScript.flippedNum == 1)
            DiamondsA.enable()
        ELSE
            DiamondsB.enable()
        ENDIF
    ELSE
        IF(mainScript.flippedNum == 1)
            SheoA.enable()
        ELSE
            SheoB.enable()
        ENDIF
    ENDIF
EndFunction
<必要なもの>
扉 NorPortcullisなど
鎖 NorPullChainなど

<設定方法>
扉のActivate Parentsに鎖を指定するだけです。
Delayについては引いた瞬間に発動させたければ0でOK。
通常の扉にLockをかけておいて初回は必ず鎖で開かせたい、というような場合は、
扉のscriptにdefault2StateActivatorを設定しましょう。
(scriptのPropertiesは何もいじらなくてOK)