This tutorial assumes a basic knowledge of the Creation Kit, and how to set up scripts. No need to write them, but you should be able to edit a few names and such to fit your follower. This could be used for a framework but the tutorial is set up for a single follower.
This is a redesigned tutorial based off of Mofakin's original one, by Jac. Morte is Jac's custom follower, a proof of concept and fully voiced follower.
The vanilla system uses a global variable to limit the player to one NPC and one animal follower. Setting up a variable for this system is not required, but if you also want to limit your framework to one NPC/animal, create a new global variable (Figure 1).
The Quest Name doesn't matter as the player will be unable to see it.
After you’ve closed and re-opened your quest, click on the scripts tab and create a new script. You can use the following as a template, adjusting to fit your mod as necessary:
If you don't want the follower to dismiss after 3 days, remove the line that RegisterForSingleUpdateGameTime()s in the Function FollowerWait().
Scriptname JacJasmineDialogQuestScript extends Quest
Actor Property PlayerREF Auto
ReferenceAlias Property FollowerAlias Auto
Faction Property DismissedFollowerFaction Auto
Faction Property CurrentHireling Auto
Message Property FollowerDismissMessage Auto
Message Property FollowerDismissMessageWedding Auto
Message Property FollowerDismissMessageCompanions Auto
Message Property FollowerDismissMessageCompanionsMale Auto
Message Property FollowerDismissMessageCompanionsFemale Auto
Message Property FollowerDismissMessageWait Auto
SetHirelingRehire Property HirelingRehireScript Auto
;Property to tell follower to say dismissal line
Int Property iFollowerDismiss Auto Conditional
Function SetFollower(ObjectReference FollowerRef)
actor FollowerActor = FollowerRef as Actor
FollowerActor.RemoveFromFaction(DismissedFollowerFaction)
If FollowerActor.GetRelationshipRank(PlayerREF) <3 && FollowerActor.GetRelationshipRank(PlayerREF) >= 0
FollowerActor.SetRelationshipRank(PlayerREF, 3)
EndIf
FollowerActor.SetPlayerTeammate()
;FollowerActor.SetActorValue("Morality", 0)
FollowerAlias.ForceRefTo(FollowerActor)
FollowerActor.EvaluatePackage()
EndFunction
Function FollowerWait()
actor FollowerActor = FollowerAlias.GetActorRef() as Actor
FollowerActor.SetActorValue("WaitingForPlayer", 1)
SetObjectiveDisplayed(10, abforce = true)
;follower will wait 3 days
FollowerAlias.RegisterForSingleUpdateGameTime(72)
EndFunction
Function FollowerFollow()
actor FollowerActor = FollowerAlias.GetActorRef() as Actor
FollowerActor.SetActorValue("WaitingForPlayer", 0)
SetObjectiveDisplayed(10, abdisplayed = false)
FollowerActor.EvaluatePackage()
EndFunction
Function DismissFollower(Int iMessage = 0, Int iSayLine = 1)
If FollowerAlias && FollowerAlias.GetActorReference().IsDead() == False
If iMessage == 0
FollowerDismissMessage.Show()
ElseIf iMessage == 1
FollowerDismissMessageWedding.Show()
ElseIf iMessage == 2
FollowerDismissMessageCompanions.Show()
ElseIf iMessage == 3
FollowerDismissMessageCompanionsMale.Show()
ElseIf iMessage == 4
FollowerDismissMessageCompanionsFemale.Show()
ElseIf iMessage == 5
FollowerDismissMessageWait.Show()
Else
;failsafe
FollowerDismissMessage.Show()
EndIf
actor DismissedFollowerActor = FollowerAlias.GetActorRef() as Actor
DismissedFollowerActor.StopCombatAlarm()
DismissedFollowerActor.AddToFaction(DismissedFollowerFaction)
DismissedFollowerActor.SetPlayerTeammate(false)
DismissedFollowerActor.RemoveFromFaction(CurrentHireling)
DismissedFollowerActor.SetActorValue("WaitingForPlayer", 0)
;hireling rehire function
HirelingRehireScript.DismissHireling(DismissedFollowerActor.GetActorBase())
If iSayLine == 1
iFollowerDismiss = 1
DismissedFollowerActor.EvaluatePackage()
;Wait for follower to say line
Utility.Wait(2)
EndIf
FollowerAlias.Clear()
iFollowerDismiss = 0
;don't set count to 0 if Companions have replaced follower
If iMessage == 2
;do nothing
EndIf
EndIf
EndFunction
Ignore the script name, and the alias name. Give the alias the name of your follower. The Specific Reference should be set to NONE, NOT TO THE ACTUAL NPC AS BELOW. If it is set to the specific ref, then your NPC will be following you from the start.
If you don't want the waiting dismissal part, remove all code between and including Event OnUpdateGameTime and EndEvent for OnUnload.
Scriptname JacJasmineDialogQuestAliasScript extends ReferenceAlias
Faction Property CurrentHireling Auto
Message Property FollowerDismissMessage Auto
Actor Property PlayerREF Auto
Event OnUpdateGameTime()
;kill the update if the follower isn't waiting anymore
If Self.GetActorRef().GetActorValue("WaitingforPlayer") == 1
; debug.trace(self + "Dismissing the follower because he is waiting and 3 days have passed.")
(GetOwningQuest() as JacJasmineDialogQuestScript).DismissFollower(0,0)
EndIf
EndEvent
Event OnUnload()
;if follower unloads while waiting for the player, wait three days then dismiss him.
If Self.GetActorReference().GetActorValue("WaitingforPlayer") == 1
(GetOwningQuest() as JacJasmineDialogQuestScript).FollowerWait()
EndIf
EndEvent
Event OnCombatStateChanged(Actor akTarget, int aeCombatState)
If (akTarget == PlayerREF)
; debug.trace(self + "Dismissing follower because he is now attacking the player")
(GetOwningQuest() as JacJasmineDialogQuestScript).DismissFollower(0, 0)
EndIf
EndEvent
Event OnDeath(Actor akKiller)
; debug.trace(self + "Clearing the follower because the player killed him.")
Self.GetActorRef().RemoveFromFaction(CurrentHireling)
Self.Clear()
EndEvent
The properties will not look the same as this picture, as the picture is before a change I made to optimize the script. You will have PlayerREF instead of the Dialog quest. That is perfectly fine, just fill the property (PlayerREF can autofill).
If you don't want the follower to be dismissed after 3 days, skip this step.
(GetOwningQuest() as JacJasmineDialogQuestScript).SetFollower(akSpeaker)
(Figures 7 & 8). Be sure to change the property/variable names to match your mod's.
To have the follower wait, the fragment is:
(GetOwningQuest() as JacJasmineDialogQuestScript).FollowerWait()
For dismissal, it’s:
(GetOwningQuest() as JacJasmineDialogQuestScript).DismissFollower(0,0)
For following after having them wait, it’s:
(GetOwningQuest() as JacJasmineDialogQuestScript).FollowerFollow()
To make your follower do something, like chop wood, attack a target, etc. it’s:
akspeaker.SetDoingFavor()
To open their inventory, it’s:
akspeaker.OpenInventory()
If you do not want your follower to have inventory access, do not add that line and have them say something to that effect.
//Edit: 8.29.16
Be sure to add your follower's Actor form - not the alias, but the Actor itself - to both PotentialFollowerFaction and DismissedFollowerFaction. You can do this by going to the Factions tab, right clicking in the list and choosing Add, then filtering for the aforementioned factions. Put them in PotentialFollowerFaction at the value of 1 and DismissedFollowerFaction at the value of 0 or 1. This means that they are in the faction where they have been "dismissed", and this will match the condition in your "Follow me, I need your help" dialogue, so that will properly show up.
And that’s it. You now have a custom follower that uses a custom framework. Check out Deck 16’s voiced follower tutorial if you want to add a custom voice to your follower.