同じことをSkyrimでもやりましたので、基本的なことはそちらにまとめてあります。
BGMの準備
Music TypeとMusic TrackはFO4Editでバニラのレコードをコピーして作ります。aaaMusCombatとしました。
Music TypeのPriorityはバニラのMUSzCombatで始まるMusic Typeを参考に7に設定しました。そしてバニラのMusic Typeは99に下げました。
| Music Type | 変更前 | 変更後 | 
|---|---|---|
| MUSzCombat~ | 6~8 | 99 | 
| aaaMUSCombat | 7 | 
クエスト
BGMを管理するQuestのスクリプトです。Questを用意してこのスクリプトを紐づけます。
ベルチバードに乗っている時にもBGMを流すようにしてみました。
BGMを再生するサンプルコード (papyrus)
Scriptname MyTweak:MusicManagerScript Extends Quest
; 戦闘用BGM
MusicType Property aaaMUSCombat Const Auto Mandatory
; ボス戦用BGM
;MusicType Property aaaMUSCombatBoss Const Auto Mandatory
Actor PlayerRef
; BGMを流すために使うスコア
Int BGMScore
Int TimerBGMStartPrepare = 1 Const	; BGMを開始するか判定
Int TimerBGMEndPrepare   = 2 Const	; BGMを終了するか判定
Int TimerCheckVertibird  = 3 Const	; ベルチバードから降りたか判定
; 最初の1回だけ行う初期化処理
Function Initialize()
    PlayerRef = Game.GetPlayer()
    RegisterForRemoteEvent(PlayerRef, "OnPlayerEnterVertibird")
    RegisterForRemoteEvent(PlayerRef, "OnPlayerLoadGame")
EndFunction
; ゲームをロードする毎に行う初期化処理
Function Initialize2()
    BGMScore = 0
    GoToState("")
    ; プレイヤーの死亡時はBGMを止める
    if Game.IsPluginInstalled("Knockout Framework.esm")
        koframeworkevents KFManagerQuest = Game.GetFormFromFile(0xF99, "Knockout Framework.esm") as koframeworkevents
        RegisterForCustomEvent(KFManagerQuest, "OnUniqueKnockOutStart")
    endif
EndFunction
; スコアを加算する
Function AddBGMScore()
    BGMScore += Utility.RandomInt(1, 10)
    ;Debug.Trace("AddBGMScore score = " + BGMScore)
    StartTimer(1.0, TimerBGMStartPrepare)
EndFunction
; ベルチバードがバニラのものか調べる
Bool Function IsVanillaVertibird(ObjectReference akVertibird)
    Debug.Trace("IsVanillaVertibird " + akVertibird)
    return Math.RightShift(akVertibird.GetBaseObject().GetFormID(), 24) <= 0x06
EndFunction
Event OnQuestInit()
    Initialize()
    Initialize2()
EndEvent
; プレイヤーがベルチバードに乗った時に呼ばれる
Event Actor.OnPlayerEnterVertibird(Actor akActorRef, ObjectReference akVertibird)
    Debug.Trace("OnPlayerEnterVertibird " + akVertibird)
    if IsVanillaVertibird(akVertibird)
        aaaMUSCombat.Add()
        GoToState("VERTIBIRD")
    endif
EndEvent
; ゲームをロードした時に呼ばれる
Event Actor.OnPlayerLoadGame(Actor akActorRef)
    Initialize2()
EndEvent
Event OnTimer(int aiTimerID)
    if aiTimerID == TimerBGMStartPrepare
        ; 戦闘がある程度続いているならBGMを開始する
        if PlayerRef.IsWeaponDrawn() && PlayerRef.IsInCombat()
            if BGMScore >= 10
                aaaMUSCombat.Add()
                GoToState("COMBAT")
            endif
        else
            BGMScore = 0
        endif
    endif
EndEvent
Event KoFrameworkEvents.OnUniqueKnockOutStart(koframeworkevents akSender, var[] Arguments)
    ; place holder
EndEvent
; 戦闘用BGMが流れている時のステート
State COMBAT
    Event OnBeginState(string asLastState)
        Debug.Trace("COMBAT.OnBeginState")
        StartTimer(5.0, TimerBGMEndPrepare)
    EndEvent
    Event OnTimer(int aiTimerID)
        if aiTimerID == TimerBGMEndPrepare
            if PlayerRef.IsWeaponDrawn()
                if PlayerRef.IsInCombat()
                    BGMScore = 10
                else
                    BGMScore -= 1
                endif
            else
                BGMScore -= 5
            endif
            ; 戦闘が終わったようならBGMを止める
            if BGMScore <= 0
                aaaMUSCombat.Remove()
                GoToState("")
            else
                StartTimer(5.0, TimerBGMEndPrepare)
            endif
        endif
    EndEvent
    Event Actor.OnPlayerEnterVertibird(Actor akActorRef, ObjectReference akVertibird)
        Debug.Trace("OnPlayerEnterVertibird " + akVertibird)
        if IsVanillaVertibird(akVertibird)
            GoToState("VERTIBIRD")
        endif
    EndEvent
    Event KoFrameworkEvents.OnUniqueKnockOutStart(koframeworkevents akSender, var[] Arguments)
        string EventName = Arguments[0] as string
        ;Actor Victim     = Arguments[1] as Actor
        ;Actor Aggressor  = Arguments[2] as Actor
        if EventName == "PlayerDefaultKnockout"
            CancelTimer(TimerBGMEndPrepare)
            aaaMUSCombat.Remove()
            BGMScore = 0
            GoToState("")
        endif
    EndEvent
    Function AddBGMScore()
        ; do nothing
    EndFunction
EndState
; ベルチバードに乗っている時のステート
State VERTIBIRD
    Event OnBeginState(string asLastState)
        Debug.Trace("VERTIBIRD.OnBeginState")
        StartTimer(5.0, TimerCheckVertibird)
    EndEvent
    Event OnTimer(int aiTimerID)
        if aiTimerID == TimerCheckVertibird
            if PlayerRef.IsOnMount()
                StartTimer(5.0, TimerCheckVertibird)
            else
                if PlayerRef.IsInCombat()
                    BGMScore = 10
                    GoToState("COMBAT")
                else
                    aaaMUSCombat.Remove()
                    BGMScore = 0
                    GoToState("")
                endif
            endif
        endif
    EndEvent
    Event KoFrameworkEvents.OnUniqueKnockOutStart(koframeworkevents akSender, var[] Arguments)
        string EventName = Arguments[0] as string
        ;Actor Victim     = Arguments[1] as Actor
        ;Actor Aggressor  = Arguments[2] as Actor
        if EventName == "PlayerDefaultKnockout"
            CancelTimer(TimerCheckVertibird)
            aaaMUSCombat.Remove()
            BGMScore = 0
            GoToState("")
        endif
    EndEvent
    Function AddBGMScore()
        ; do nothing
    EndFunction
EndState
スペル
周囲のActorに配布するスクリプトです。Cloakスペルを用意してスペルを配布、そのMagic Effectにこのスクリプトを紐づけます。
別の処理も含まれています。
BGMを再生するサンプルコード2 (papyrus)
Scriptname MyTweak:DetectEntryScript Extends ActiveMagicEffect
; BGM管理クエスト
MyTweak:MusicManagerScript Property MusicManager Const Auto Mandatory
; 死体に入れるトークン
Weapon Property aaaCorpseToken Const Auto Mandatory
; NPCかどうかを判定するためのキーワードリスト
FormList Property NPCKeywordList Const Auto Mandatory
Actor Property PlayerRef Const Auto Mandatory
Actor MySelf
; スペルが切れたらTrueになる
Bool IsFinished
; スタック検知用に座標を保存する
Float LastX
Float LastY
Float LastZ
; スタック検知用カウンター
Int StuckCounter
; タイマー識別ID
Int TimerDetect  = 1 Const	; 攻撃ヒット後の判定
Int TimerRestart = 2 Const	; OnHitイベントを再開する
Int TimerStuck   = 3 Const	; スタック検知
Event OnEffectStart(Actor akTarget, Actor akCaster)
    ; スペルが速攻で切れてしまう時があるので様子をみる
    Utility.Wait(1.0)
    if IsFinished
        return
    endif
    MySelf = akTarget
    if MySelf.IsDead()
        GoToState("DEAD")
    else
        RegisterForHitEvent(MySelf, PlayerRef)
        ; NPCでないなら非NPCのステートへ
        ; NPCならステートはNoneのまま
        if !MySelf.HasKeywordInFormList(NPCKeywordList)
            GoToState("NOT_NPC")
        endif
    endif
EndEvent
Event OnEffectFinish(Actor akTarget, Actor akCaster)
    IsFinished = true
EndEvent
Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, \
    bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)
    ; しばらくはOnHitイベントを受け取らないようにして負荷対策する
    UnregisterForAllHitEvents()
    ; 3秒後に判定する
    ; 3秒後にする理由は、銃声がやむのを待つのと、即死したかどうかを判断するため
    StartTimer(3.0, TimerDetect)
EndEvent
Event OnDying(Actor akKiller)
    UnregisterForAllHitEvents()
    CancelTimer(TimerStuck)
    GoToState("DEAD")
    ;MySelf.CreateDetectionEvent(PlayerRef, 100)
EndEvent
Event OnDeath(Actor akKiller)
    UnregisterForAllHitEvents()
    CancelTimer(TimerStuck)
    GoToState("DEAD")
    ;MySelf.CreateDetectionEvent(PlayerRef, 100)
EndEvent
Event OnActivate(ObjectReference akActionRef)
    ;Debug.Trace(Self + " OnActivate")
    if MySelf.IsDead()
        GoToState("DEAD")
    endif
EndEvent
Event OnTimer(int aiTimerID)
    ;Debug.Trace(Self + " OnTimer " + aiTimerID)
    if aiTimerID == TimerDetect
        ;if MySelf.IsHostileToActor(PlayerRef)
            MusicManager.AddBGMScore()
        ;endif
        ; 30秒後から再びOnHitイベントを受け付ける
        StartTimer(30.0, TimerRestart)
    elseif aiTimerID == TimerRestart
        RegisterForHitEvent(MySelf, PlayerRef)
    elseif aiTimerID == TimerStuck
        ;Debug.Trace(Self + " OnTimer " + aiTimerID)
        ; 座標が変化していないならカウンターを増やす
        if Math.abs(LastX - MySelf.X) < 10.0 && Math.abs(LastY - MySelf.Y) < 10.0 && Math.abs(LastZ - MySelf.Z) < 10.0
            StuckCounter += 1
        else
            StuckCounter = 0
        endif
        LastX = MySelf.X
        LastY = MySelf.Y
        LastZ = MySelf.Z
        if StuckCounter >= 5
            ; スタックしているようなら戦闘を強制的に中断させる
            ;Debug.Trace(MySelf + " is stucked")
            Actor Victim = MySelf.GetCombatTarget()
            if Victim
                MySelf.StopCombat()
                Utility.Wait(0.5)
                MySelf.StartCombat(Victim)
            endif
            StuckCounter = 0
        elseif StuckCounter
            StartTimer(1.0, TimerStuck)
        else
            StartTimer(5.0, TimerStuck)
        endif
    endif
EndEvent
State NOT_NPC
    Event OnCombatStateChanged(Actor akTarget, int aeCombatState)
        ;Debug.Trace(Self + " OnCombatStateChanged " + aeCombatState)
        ; プレイヤーを戦闘を開始したのならスタックの検知を開始する
        if akTarget == PlayerRef
            if aeCombatState == 1
                StartTimer(1.0, TimerStuck)
            else
                CancelTimer(TimerStuck)
            endif
        endif
    EndEvent
EndState
State DEAD
    Event OnActivate(ObjectReference akActionRef)
        ;Debug.Trace(Self + " DEAD.OnActivate")
        ; プレイヤーがアクティベートしたら死体にトークンを入れる
        if akActionRef == PlayerRef
            MySelf.AddItem(aaaCorpseToken, abSilent = true)
            Dispel()
        endif
    EndEvent
    Event OnTimer(int aiTimerID)
        ;Debug.Trace(Self + " DEAD.OnTimer")
    EndEvent
EndState
  
  
  
  
