Jump to content

[LE] Mods and save games


Recommended Posts

It's common knowledge now that scripts are hode-coded (baked) into a save file when you add an esp onto your game. Therefore deleting it during a throughout can cause papyrus errors and constant searches for properties that no longer exist.

 

But why can't you just delete those scripts added by the mod from the data folder. I've heard this doesn't work and I don't really know why.

 

As for updating, as a mod author how does one go about this if the information has been baked into the save? For example, If I want to patch a bug which would require new scripting, could I remove the old script, replace them with new one and have them swap the old .esp for the new .esp or is the save already "tainted" and would they have to start afresh?

Link to comment
Share on other sites

Scripts don't exist on their own. They are always attached to some game object (quest, alias, reference, perk, magic effect, etc.). For each object with an attached script the game records the script name, current values of its properties and global variables, and a copy of the code of any actively running functions in the save file. One of the absolute worst things you can do is to delete the script (and that's especially true if any function of that script was running at the time the save file was created).

When the game gets reloaded it tries to restore the related script state information as it loads each object back into the game so that the script can continue operating without interruption. If the script is missing the game will generate errors because it has stored information and now no place to use it. And if that script was registered for events then you'll get errors every time that event triggers since there's no script to handle it anymore.

As for updating scripts, never remove an existing script and then attach another in its place. You can modify existing scripts in many ways and fix almost any bug in the script except for an endless loop. The problem with the infinite loop is that when a function (or event) starts running it is copied into memory and then saved with the game. If you change the body of that function it won't change the copy that's currently running so if it's in an infinite loop it will stay in that loop and there's no way to break out.

On the other hand even an "infinite" RegisterForUpdate() sequence can be broken by simply editing the OnUpdate() event and adding a call to UnregisterForUpdate() so that only one final update will be made after the game reloads.

The biggest problem people have is that they want to change the value of some property that wasn't filled correctly. Once a script has been loaded the values of all of its properties are stored, so any changes you make to default property assignments won't be seen. But you can always add additional properties and then switch out the code in the various functions and events to use the new property instead of the old one.

Here's a quick example of a bad script that should really be changed.

ScriptName NagMeScript extends Quest

Message Property MyDailyReminderMessage Auto

float Property frequency = 24.0 Auto

Event OnInit()
    RegisterForUpdate(frequency)
EndEvent

Event OnUpdate()
    MyDailyReminder.Show()
EndEvent

Now assume the problem is that you thought you were registering for RegisterForUpdateGameTime and 24.0 was the number of in-game hours. But as written the script is really popping up the message every 24 seconds and doesn't care if you're in the middle of battle.

There are many things you can change in this case but a few that you can't. You can't change the values of MyDailyReminderMessage or frequency. If this is on a quest that can't be stopped and restarted (which is very common) then that OnInit() function will never run again.

But in this particular case since there's an on-going update you can change just about everything by simply adding new properties and changing the code in OnUpdate().

The simplest thing might be that you've changed your mind completely and just want to get rid of the message.

ScriptName NagMeScript extends Quest

Message Property MyDailyReminderMessage Auto

float Property frequency = 24.0 Auto

Event OnInit()
    ; this is empty now because we wouldn't want to start things for any new players
EndEvent

Event OnUpdate()
    UnregisterForUpdate()  ; just unregister because we really don't want to do anything after all
EndEvent


Or you might want to get more complicated by changing to a weekly reminder that displays as a message box (but only when not in combat and not sneaking) and lets the player choose between two options. The trick here is that you'll need new properties since you can't change the values of the old ones. You'll also need to unregister for the old 24 second updates for anyone upgrading from the old version. For people loading this up for the first time you'll need to change the OnInit event to use your new defaults. And finally you put your new logic into the OnUpdate block and on the next update event you'll see the new behavior.

ScriptName NagMeScript extends Quest

Message Property MyDailyReminderMessage Auto
Message Property MyWeeklyReminderMessage Auto

float Property frequency = 24.0 Auto
float Property weeklyfrequency = 168.0 Auto  ; hours per week

Actor Property PlayerRef Auto

Event OnInit()
    RegisterForSingleUpdateGameTime(weeklyfrequency)  ; only works for new users
EndEvent

Event OnUpdate()
    UnregisterForUpdate()  ; handles upgrade for old users but doesn't hurt anything for new ones
    if PlayerRef.IsInCombat() || PlayerRef.IsSneaking()
        RegisterForSingleUpdate(2.0) ; check back in two seconds to see if combat/sneaking is done
    else
        int choice = MyWeeklyReminder.Show()
        if choice == 1
             ; Do something because the player pressed the second button
        else ; choice was 0
             ; Do something else for the first button
        endif
        RegisterForSingleUpdateGameTime(weeklyfrequency)
    endif
EndEvent

Technically you can also remove the old (now unused) properties. You'll get a Papyrus errors about the missing properties on the first load of a save made with the old code, but after saving and reloading the game with the new script those messages will go away because the game will throw away the stored values for missing properties. (It won't through away entire scripts though, which is why you never entirely delete scripts.)

 

When completely rewriting Inigo's scripts I cleared out some old and unwanted scripts by simply changing the code to a single OnUpdate that unregistered itself. That's as close as you can come to properly removing a Papyrus script for a save file. But note that if you do that you also need to make sure you update the properties in the CK for the objects that have that script because if not the game will try to fill the old properties each time the game loads and you'll get errors.

ScriptName NagMeScript extends Quest

Event OnUpdate()
    UnregisterForUpdate()
EndEvent
Link to comment
Share on other sites

Wow. Thanks a lot for the extensive answer. I'm going to bookmark it if when I get stuck again. I needed that explanation more than I though I did.

 

Technically you can also remove the old (now unused) properties. You'll get a Papyrus errors about the missing properties on the first load of a save made with the old code, but after saving and reloading the game with the new script those messages will go away because the game will throw away the stored values for missing properties. (It won't through away entire scripts though, which is why you never entirely delete scripts.)

 

 

 

So does that mean that for the initial script where you "changed your mind" could you delete those properties? So, in general / as a rule of thumb am I to understand you can remove properties that are completely unused?

 

But note that if you do that you also need to make sure you update the properties in the CK for the objects that have that script because if not the game will try to fill the old properties each time the game loads and you'll get errors.

 

 

I don't quite get what this means.

 

So for example If I had a script attached to furniture so when its used by the player it adds an enchantment to an item, I would have a property of the Player, the enchantment to add, and the item to add it to.

Going by what you said, If I wanted to scrap my idea, I would have to go to all the all references of furniture I placed in the CK, and delete them accordingly or what?

Link to comment
Share on other sites

Yes, you could change my original broken script and delete the original properties.

ScriptName NagMeScript extends Quest

Message Property MyWeeklyReminderMessage Auto

float Property weeklyfrequency = 168.0 Auto  ; hours per week

Actor Property PlayerRef Auto

Event OnInit()
    RegisterForSingleUpdateGameTime(weeklyfrequency)  ; only works for new users
EndEvent

Event OnUpdate()
    UnregisterForUpdate()  ; handles upgrade for old users but doesn't hurt anything for new ones
    if PlayerRef.IsInCombat() || PlayerRef.IsSneaking()
        RegisterForSingleUpdate(2.0) ; check back in two seconds to see if combat/sneaking is done
    else
        int choice = MyWeeklyReminder.Show()
        if choice == 1
             ; Do something because the player pressed the second button
        else ; choice was 0
             ; Do something else for the first button
        endif
        RegisterForSingleUpdateGameTime(weeklyfrequency)
    endif
EndEvent

But after making that script change you'll need re-fill the properties for all objects using that script in the CK. In that particular case you would need to anyway since you'll need to fill the three new properties, so it's not really any extra work. If you were just trying to clear out a now useless script then you'll get constant Papyrus warnings about missing properties unless you go update the properties on all instances of things that use that script.

 

It's best to attach scripts to the base object whenever possible rather than individual object references. It's especially useful for the cases where you change your mind later and need to change the properties. When it works to attach the script to the base object (which isn't always possible) then changing the properties on that single base object automatically applies the changes to every copy of it placed into the game. But I once needed to change the properties for an object placed in over 60 places individually and it was definitely not fun. Part of the way through I started to question whether it was worth it just to get rid of the Papyrus error messages.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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