Jump to content

How to set a condition for mod content to be implemented


Recommended Posts

Hi,

 

I'm working on my first mod using the Creation Kit. I'm getting rid of the debris in settlements, starting with Sanctuary, and want to set two conditions so that 1) the settlement needs to have 10 or more settlers and 2) 48 in-game hours need to have passed in order for the debris to be cleared, with a message appearing that says, "The people of [settlement name] have cleaned up their settlement!"

 

Is this possible, and can this be done in the Creation Kit interface? Does it need to be done in Papyrus? I write PHP/HTML/CSS and can read/dabble in other languages, I just need a starting point if using Papyrus is necessary, as I'm totally new to this and haven't been able to find any information on setting conditions in the CK.

 

Any pointers would be much appreciated!

Link to comment
Share on other sites

For future reference for all things Creation Kit, go here.

 

So first things first, you would have to set up a quest to oversee all that. Go to Character > Quest and make a new quest name it whatever you want give it a priority of around 50 or so then hit OK and open it up again. Go to the Scripts tab find the [New Script] entry, then hit OK. Name it whatever you want and hit OK. Another box will pop up, just hit Cancel. Then right click the script and hit open in external editor. this will open up your text editing program of choice (I use Notepad++. Note that there are instructions on the site that I linked on how to set up syntax highlighting and other useful stuff for making scripts).

 

You with me so far?

 

Oh, and do you want the 48 hours to pass after you get at least 10 settlers or do you need to do something else (like talk to someone or something) in order for the countdown to start?

Link to comment
Share on other sites

Scriptname MYSCRIPTNAME Extends Quest

Quest Property WorkshopParentScript Auto Const Mandatory ; this is the script that holds a lot of general workshop properties
Quest Property WorkshopScript Auto Const Mandatory ; this is the script that holds NPC Data

Message Property DebrisClearMessage Auto

Int DebrisClearingTimer = 14 ;you can use any number here just make sure it matches the second number in StartTimerGameTime
Int SettlerCount = 0
ObjectReference[] Settlers = WorkshopParentScript.GetWorkshopActors(WorkshopScript workshopRef)

While (SettlerCount < Settlers.length)
SettlerCount += 1
EndWhile

If SettlerCount >= 10 ;if the population is 10 or more
StartTimerGameTime(48, DebrisClearingTimer) ;starts a timer for 48 hours in-game
EndIf

Event OnTimerGameTime(DebrisClearingTimer) ;when the timer expires, show the message
DebrisClearMessage.Show()
EndEvent 

This is just an example script. You would still have to make the message (go to Miscellaneous > Message). If you want to use the settlement's name in the message you would have to use text replacement.

 

Hope this helps!

Link to comment
Share on other sites

Thanks! That's very helpful.

 

How does this line know which settlement's settlers it is counting:

ObjectReference[] Settlers = WorkshopParentScript.GetWorkshopActors(WorkshopScript workshopRef)

Is that counting the total of all settlement's settlers? I'm looking through the WorkShopParentScript.psc and WorkshopScript.psc files but it's not yet apparent to me how this works. I'm not clear on how this example script knows which settlement it's dealing with. Do I need to actually do something a lot more specific, as talked about here?

 

Also, in addition to displaying a message, what I'm looking for is to remove a bunch of objects from the settlement in the same Event that triggers the message. Would I need to go into the Creation Kit and get reference ID's for all of the objects that I want to remove, and then add those to an array (for example) and iterate through that with a command to remove all of the objects from the game? For example, using ObjectReference Disable?

 

As for displaying the message, does the message ID that I create in the CK need to match the Message Property that I'm setting in the script (in this example, DebrisClearMessage)?

 

For text replacement, there's not anything in the above linked documentation that talks about locations. Would I use <Alias=SanctuaryHillsLocation> for just Sanctuary?

Link to comment
Share on other sites

the parameter for GetWorkshopActors (in parentheses), WorkshopScript workshopRef can be thought of as the workshop's location.

 

Wait, did you want to get the total number of settlers for ALL your settlements or just a specific settlement? Because the above example was just for a specific settlement.

 

For the message, it doesn't have to match, but then you would have to fill it out using the property form.

 

For text replacement you would use <Alias=MyMessageName> where MyMessageName is the Editor ID of the message. Then, in the Message Title and Message Body, you would put what you want to replace it with - Sanctuary, for example. Also, make sure that all of your messages are associated with the quest you created that manages all this (there's a dropdown menu in the form you use to create a message).

Link to comment
Share on other sites

OK, so I have this:

Scriptname CleanSettlement extends Quest
{A test script to display a message 48 hours after a settlement has obtained at least 10 settlers.}

Quest Property WorkshopParentScript Auto Const Mandatory
Quest Property WorkshopScript Auto Const Mandatory

Message Property CleanSettlementMsg Auto

Int CleanSettlementTimer = 48
Int SettlerCount = 0
ObjectReference[] Settlers = WorkshopParentScript.GetWorkshopActors(WorkshopScript workshopRef)

While (SettlerCount < Settlers.length)
SettlerCount += 1
EndWhile

If SettlerCount >= 10 ;if the population is 10 or more
StartTimerGameTime(48, CleanSettlementTimer) ;starts a timer for 48 hours in-game
EndIf

Event OnTimerGameTime(CleanSettlementTimer) ;when the timer expires, show the message
Debug.MessageBox("The event is triggering this message.")
CleanSettlementMsg.Show()
EndEvent

When I compile, it fails, throwing these errors:

 

C:\Users\...\AppData\Local\Temp\PapyrusTemp\CleanSettlement.psc(11,29): no viable alternative at input 'WorkshopParentScript'
C:\Users\...\AppData\Local\Temp\PapyrusTemp\CleanSettlement.psc(11,49): required (...)+ loop did not match anything at input '.'
C:\Users\...\AppData\Local\Temp\PapyrusTemp\CleanSettlement.psc(11,18): Unknown user flag workshopparentscript
No output generated for CleanSettlement, compilation failed.

 

What are the numbers in parentheses? I'm accustomed to seeing line and column numbers but the above doesn't correspond to what I've got in my file.

 

As for the message, I'm not sure that I understand what you're suggesting. Where would I use <Alias=MyMessageName>? And if I have to use the name of the location in my message body, what's the point in text replacement? At the moment I've added a Location Alias to the Quest, associated it with SanctuaryHillsLocation, and used the Alias, "Sanctuary." In my message, titled CleanSettlementMsg (so its content should be displayed by the above script, right?), I have this:

 

The people of <Alias=Sanctuary> have cleaned up their settlement!

 

Of course, I still have no idea how I would get the message to dynamically show any location.

Link to comment
Share on other sites

The numbers are line and column numbers. the (11, 29) would refer to the WorkshopParentScript.GetWorkshopActors(WorkshopScript workshopRef), which is on the 11th line of the file and starts at the 29th column.

 

Hmm... ugh, I'm not very good at debugging (or writing) scripts, but try replacing the Quest property lines with:

 

import WorkshopParentScript
import WorkshopScript

 

and then the 11th line would be something like (I think):

 

ObjectReference[] Settlers = GetWorkshopActors(WorkshopScript workshopRef)

 

If you keep getting errors, just keep changing things until it works (remember, google is your friend). That's what I usually do anyway...

 

As for the text replacement, I misspoke when I said to fill in the message body and title. As per a quote from the wiki page:

You can use a Message to replace the display name of anything in a quest's alias.
 Select a message on the "Display Name" dropdown on the Alias window.
 The Title of the Message is used instead of the alias's normal name.
 The Message can use text replacement (see above), including the base name of the aliased object.
 This is how you'd rename a sword to "Bob's Iron Longsword" for example.
Link to comment
Share on other sites

Thanks for your help so far. I really appreciate it. :) These last few days I've been doing a lot of reading and watching videos to learn how this whole thing works, and I've found some useful threads here on the Nexus as well. I'll post my solution once I have it hand. Almost there...

Link to comment
Share on other sites

So, I've answered the basic question that I was looking to answer. My script compiles successfully but I'm still having troubles capturing the right data. Since that's a different issue than what I posted about, I'll start a new thread for a different question. For reference, this is what I have so far:

Scriptname CleanSettlementScript extends Quest
{This script automates settlers removing brambles and debris from their settlements.}

Import Keyword

WorkshopParentScript Property WorkshopParent Auto
WorkshopScript Property WorkshopScriptRef Auto
WorkshopScript[] workshopList

Keyword Property QuestEvent Auto

Message Property CleanSettlementPreMsg Auto
{This message alerts the player to a given settlement that will be cleaned.}

Message Property CleanSettlementMsg Auto
{This message alerts the player to a given settlement that has been cleaned.}

; The time, in game-time hours, between checking for settlements to queue.
float checkTimer = 0.3 const
; The time, in game-time hours, between queuing a settlement and cleaning it.
float cleanTimer = 0.3 const
; The timerID for checking for settlements to queue. Used in OnTimerGameTime().
int checkTimerID = 1 const
; The timerID for cleaning settlements in the queue. Used in OnTimerGameTime().
int cleanTimerID = 2 const
; An object holding the settlement queued for cleaning.
WorkshopScript queuedSettlement
; The minimum number of settlers needed to trigger cleaning the settlement.
int settlerGoal = 10 const
; The number of settlers in a given settlement.
int settlerCount = 0
; The number of settlements.
int workshopCount = 0
; A list of cleaned settlements.
int[] cleanList


; Start up the plugin.
Event OnInit()
    debug.trace(self + "OnInit")
    ; A list of settlements.
    workshopList = GetWorkshopList()
    workshopCount = workshopList.length
    ; A list of cleaned settlements.
    cleanList = new int[workshopCount]
    StartTimerGameTime(checkTimer, checkTimerID)
EndEvent

; Calls the CheckForSettlementsToClean() and CleanSettlement() functions.
Event OnTimerGameTime(int inputTimerID)
    debug.trace(self + "OnTimerGameTime")
    if inputTimerID == checkTimerID ; Check for settlements to clean.
        CheckForSettlementsToClean()
    elseif inputTimerID == cleanTimerID ; Clean queued settlement.
        CleanSettlement()
    endif
EndEvent

function CheckForSettlementsToClean()
    debug.trace(self + "CheckForSettlementsToClean")
    int i = 0
    bool break = false
    while (i < workshopCount)
        if !break
            if ShouldCleanSettlement(workshopList[i])
                QueueSettlement(workshopList[i])
                break = true
            endif
            i += 1
        else
            i = workshopCount
        endif
    endWhile
    StartTimerGameTime(checkTimer, checkTimerID)
endFunction

; Calls the DisableObjects() function and displays a message.
function CleanSettlement()
    debug.trace(self + "CleanSettlement")
    if queuedSettlement
        DisableObjects(queuedSettlement)
        SetQueuedSettlement(NONE)
        CleanSettlementMsg.Show()
        QuestEvent.SendStoryEvent(queuedSettlement.myLocation)
    endif
endFunction

; Disables objects for the provided settlement.
function DisableObjects(WorkshopScript inputSettlement)
    debug.trace(self + "DisableOBjects")
    ; Stuff goes here
endFunction

; Queues settlement for cleaning.
function QueueSettlement(WorkshopScript inputSettlement)
    debug.trace(self + "QueueSettlement")
    SetQueuedSettlement(inputSettlement)
    StartTimerGameTime(cleanTimer, cleanTimerID) ; A timer ID of 1 is used for queued settlements.
    RecordQueuedSettlement(inputSettlement)
    CleanSettlementPreMsg.Show()
    QuestEvent.SendStoryEvent(inputSettlement.myLocation)
endFunction

; Records queued settlement in cleanList array.
function RecordQueuedSettlement(WorkshopScript inputSettlement)
    debug.trace(self + "RecordQueuedSettlement")
    int arrayIndex = workshopList.Find(inputSettlement)
    cleanList[arrayIndex] = arrayIndex
endFunction


; Checks to see if settlement should be queued for cleaning.
bool function ShouldCleanSettlement(WorkshopScript inputSettlement)
    debug.trace(self + "ShouldCleanSettlement")
    if GetSettlerCount(inputSettlement) >= settlerGoal && !SettlementIsClean(inputSettlement)
        return true
    else
        return false
    endif
endFunction

; Checks whether settlement has already been stored in the cleanList array.
bool function SettlementIsClean(WorkshopScript inputSettlement)
    debug.trace(self + "SettlementIsClean")
    int arrayIndex = workshopList.Find(inputSettlement)
    if cleanList.Find(arrayIndex) < 0
        return false
    else
        return true
    endif
endFunction

; Gets the queued settlement.
WorkshopScript function GetQueuedSettlement()
    return queuedSettlement
endFunction

; Gets the settlement location.
; function GetSettlementLocation(inputSettlement)
    
; endFunction

; Gets the settlerCount for the inputSettlement.
int function GetSettlerCount(WorkshopScript inputSettlement)
    debug.trace(self + "GetSettlerCount")
    WorkshopDataScript:WorkshopRatingKeyword[] ratings
    WorkshopScriptRef = inputSettlement as WorkshopScript
    if (WorkshopScriptRef)
        WorkshopParent = WorkshopScriptRef.WorkshopParent
        if (WorkshopParent)
            ratings = WorkshopParent.WorkshopRatings
            settlerCount = WorkshopScriptRef.GetBaseValue(ratings[WorkshopParent.WorkshopRatingPopulation].resourceValue) as int
        endif
    endif
    ratings = NONE
    return settlerCount
endFunction

; Sets the queued settlement
function SetQueuedSettlement(WorkshopScript settlementToQueue)
    queuedSettlement = settlementToQueue
endFunction

; Return workshopList.
WorkshopScript[] function GetWorkshopList()
    debug.trace(self + "GetWorkshopList")
    if workshopList.length <= 0
        SetWorkshopList()
    endif
    return workshopList
endFunction

; Populate workshopList.
WorkshopScript[]  function SetWorkshopList()
    debug.trace(self + "SetWorkshopList")
    workshopList = WorkshopParent.Workshops
endFunction

As for the Quest, under Quest Data in the CK, I selected Script Event under the Event drop-down menu, and under Quest Aliases I created an Alias called myLocation. For that, I selected as the Fill Type "Find Matching Location," checked "From Event," and under the Event Data drop-down I selected Location. This is fed to the Quest by the SendStoryEvent() function.

 

For getting Messages to show, I followed Bethesda's tutorial. Curiously, it's not on the Message page, but on a more or less random one. In the message, I used <Alias=myLocation> to grab the Quest Alias that I created as described above.

 

I am using the WorkshopGunnerAttack01 Quest as a guide/example for how to do some of this.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...