Welcome to the AFK Mods bug tracker. In order to report an issue, you need to have a VALIDATED account to post one. Once you have followed the link the registration email sent you, please select a project from the drop down menu below. Select “Open New Issue” and fill out the form with as many details as possible.

Please also consider sending your bug report to Bethesda if you are reporting on an issue with Skyrim Special Edition, Fallout 4, or Starfield. Doing so will help everyone on all platforms.

Issue Data
Status: Closed
Issue Type: Bug Report
Project: Unofficial Skyrim Legendary Edition Patch
Component: Skyrim LE: Vanilla
Category: Quests
Assigned To: Nobody
Platform: Windows PC
Severity: Low
Votes: 1
Watching: N/A
Opened By Arthmoor on Jul 1, 2012 6:00 am
Closed By Arthmoor on Jan 22, 2013 12:07 am
Resolution: Fixed

Issue #181: Khajiit tent camp leaving Ahkari behind

 
There is atleast one Khajiit tent camp that moves around between the holds at intervals. Don’t know exactly where they move, don’t know at the intervals, don’t know if there is one or several different camps going at once (but I recall there as being one or possibly two).

Regardless, I do know that when a city is NOT visited by any Khajiit traders, when their campground by the stables is empty and abandoned (not a single thing to be found, just grass and a cold fireplace), then there should be NO members of the Khajiit traders still lurking about.

This however was not the case as I fast travelled to Riften. I travelled to the stables and not into the city just so I could see if the Khajiit were hanging out there. And to my amazement, they clearly were not there – except for one of their traders, Ahkari! She had all the regular dialogue, she was trading and she wandered about the campsite all alone.

Seems that she was left behind somehow when the code to move the camp triggered. I wonder if she’d be missing from wherever they currently are if I went there…

Related Issues: 902  31576  

Comments

19 comment(s) [Closed]
Sclerocephalus said:
 
(In reply to comment #2)

It's true that I have been looking into the caravan scripts some time ago. I had indeed several modified versions running on my system to deal with several potential problems I've spotted in the Caravan main scripts. Many of them were working fine, i.e. they compiled without error and did not show any unwanted effects in the game, so they are doing no harm at least. Whether they really cure all bugs or not is difficult to say, though. This is because the various bugs are not predictable (well, at least one glitch can be triggered, as will be explained below) and also do not occur often (at least not in an un-modded game). Compared to my first playthrough with more than 1,200 hours of real-time play, where I occasionally witnessed some of the bugs myself, my play time with the modified scripts is very small. What I can say though is that there are good reasons for every modification (I will explain them below) and that they remove several bugs - it's just not clear whether I caught them all.

You're also right in that I should share the modifications. Though, after spending much time on this in spring this year, I didn't have much time for further work during the last months and I never made it to combine all useful modifications in one script (don't think that I found all the right solutions at once; several attempts had to be discarded again because they had no effect at all). I also still owe you a reply to an issue in another thread about the caravan script problems (I'm very sorry for the big delay).

I also never had a closer look at the Caravan Leader script. While doing so two days ago, I discovered that there's also a severe logics bug, in addition to being not multi-threading safe. Meanwhile, I have conceived a solution for this problem (note however that this modification, unlike the other modifications, has not yet been play-tested) and rewritten all related scripts to combine all useful modifications and additions. Most changes have been made to the Caravan main script. I have made sure that it will compile without error (tested in the CK), but this version has not yet been play-tested either. Although not very likely (since the individual parts did work well on their own), it might not be working as intended (the whole is not always the sum of its parts ...). If so, please tell me and I'll fix it.


(1) Caravan Leader Script

First, I moved all of the code from the OnDeath event into a separate function (combined with the promote function of the original script) in order to make sure the script is multi-threading safe. I then did modify the code so as to use an array of object references, which largely eliminates the if-then cascades.

As is obvious from the original script, a dead leader is replaced by follower1, this in turn by follower2, the latter by follower3 etc. Dead followers are not appropriately considered, though, which leads to some problems. Before going into details, it has to be noted that all of the caravans quest's reference aliases have been set up as "not optional", which means that they can't be cleared. That is, the aliases keep on pointing to actors even if those actors are dead. For the same reason, the promote function in the original script checks for a valid reference to fill with: "If RefToFillWith ..." is false for follower4 (it doesn't exist in any of the caravans) and for follower3 of caravan B (which doesn't exist either). Their reference is "none", which must be prevented from being filled into one of the existing aliases, since any attempt to do so would crash the game immediately.

Consider a case where follower1 dies and then the leader. To keep things as simple as possible, we assume that this happens with caravan B (which has only two followers):

- When follower1 dies, nothing happens. Its reference alias keeps on pointing to the dead actor, but his AI won't be processed because the "Allow dead" option is not ticked. So far, so good.
- When the leader dies, his reference alias is forced to point to the actor the follower1 alias is pointing to. Since this actor is dead, the leader remains dead (and will be forever, because the OnDeath event doesn't trigger again): Although follower2 is still alive, the caravan stops working (the "Allow dead" option is not ticked for the leader alias). Depending on where the leader died, follower2 will do nothing or wander around the editor location, because his only AI packages are to follow the caravan leader and to wander around at camp sites. Not good.
- In the next step of the OnDeath event of the original script, the follower1 alias will be forced to point to the actor the follower2 alias is still using. Now, two follower aliases are pointing to the same living actor. It's difficult to predict whether this will cause trouble (after all, all followers have the same AI packages), but it doesn't seem right. Not good either.
- It's not done here. While the dead actor the leader alias is pointing to isn't processed by the engine, the alias may trigger the UpdataCaravan function again (via the Caravan Leader script), depending on whether it was registered for an update before the leader was killed, and, since the alias can be registered for another update from within the UpdateCaravan function, this may go on for some time, with all sorts of unwanted results, including disabling of camps at sites which the remaining members never leave and enabling camps at sites where no one ever arrives. Not good at all.

In order to handle this properly, it is preferrable to set up all follower aliases as "optional", so the aliases can be cleared once the respective actor is dead and nobody left to replace him. The leaders better keep their "not optional" status though. Note that the modified Caravan main script (source code attached) will crash the game when the follower aliases remain "not optional". If there are good reasons for keeping the parameters as they are (after all, I might have overlooked something), please tell me and I will rewrite the script.

Also, while the OnDeath event can be handled entirely from within the Caravan Leader script, things are more easily dealt with when the followers react to their own OnDeath events separately. Once the arrays are implemented (finally, with reference aliases rather than object references), the code is basically the same, except for different array indices. Thus, I have moved the function to the Caravan Script, so it can be called both from the Caravan Leader script and from a new Caravan Follower script, which needs to be attached to the follower aliases. The source codes of these two scripts (both very handy) are provided below:


**************************************************************************

ScriptName CaravanLeaderScript Extends ReferenceAlias Conditional

Int Property CaravanNumber Auto
(0: Caravan A, 1: Caravan B, 2: Caravan C)

;-------------------------------------------------------------------------

Event OnUpdateGameTime()

Actor LeaderRef = GetActorReference()

(GetOwningQuest() as CaravanScript).UpdateCaravan (CaravanNumber, LeaderRef, WeAreDisablingCamp = True)

EndEvent

;-------------------------------------------------------------------------

Event OnDeath (Actor akKiller)

(GetOwningQuest() as CaravanScript).UpdateAliases (CaravanNumber, 0)

EndEvent

**************************************************************************

ScriptName CaravanFollowerScript Extends ReferenceAlias Conditional

Int Property CaravanNumber Auto
(0: Caravan A, 1: Caravan B, 2: Caravan C)

Int Property MemberIndex Auto
(0: Leader, 1: Follower 1, 2: Follower 2, 3: Follower 3)

;-------------------------------------------------------------------------

Event OnDeath (Actor akKiller)

(GetOwningQuest() as CaravanScript).UpdateAliases (CaravanNumber, MemberIndex)

EndEvent

**************************************************************************

It's actually surprising why the OnDeath event does not appear to cause massive glitches (it certainly has the potential). In an un-modded game, the caravan guys rarely ever die (and thus the code rarely ever runs). Keeping this in mind, isn't it suspect that the caravans' routes are carefully set up to dodge around all delicate areas ?

In a modded game, with enemies added which the caravan guys are not set up to deal with, they can be killed more easily and the glitches will occur more often. Thus, although the original script is bugged, we should not forget that there are also mod conflicts at work here.


(2) Script fragments attached to travel packages:

Since I'm using arrays throughout the modified Caravan main script (they make the code more handy, reduce the script length significantly and save lots of if-then cascades), it was preferrable to assign the values 0-2 to the caravans A-C (instead of 1-3 as in the original script), but this also required to modify the script fragments calling the main script from the travel packages. While I was at it, I also removed a superfluous parameter from the UpdateCaravan function (its only use was in a debug message).

All six fragments have only a single line of code. The modified versions are listed below:

**************************************************************************

PF_CaravanLeaderATravel1_00073CD6 & PF_CaravanLeaderATravel2_00073CF0:
(GetOwningQuest() as CaravanScript).UpdateCaravan (0, akActor, WeAreEnablingCamp = true)

PF_CaravanLeaderBTravel1_00072FEA & PF_CaravanLeaderBTravel2_00073FA1:
(GetOwningQuest() as CaravanScript).UpdateCaravan (1, akActor, WeAreEnablingCamp = true)

PF_CaravanLeaderCTravel1_00073CC0 & PF_CaravanLeaderCTravel2_000734C8:
(GetOwningQuest() as CaravanScript).UpdateCaravan (2, akActor, WeAreEnablingCamp = true)

**************************************************************************


(3) Caravan main script:

There are major changes due to the use of arrays (including a new OnInit event to initialize them at game start), but they improve the code significantly. In addition, they also make it more easily legible (and, in my opinion, also more easily understandable - just compare the remaining functions with their versions in the original script).

I also removed the debug messages (none of them was helpful in locating the actual bugs) and the comment lines (well, they provide lots of information, but the most important information, i.e. the one that finally led me to recognize what was the likely reason for THE bug, was not included - one of Murphy's laws for scientific instruments comes into mind: the accessibility of a part is inversely proportional to the probability of its failure).

The function GetLocationGlobal (the one that got me puzzled the first time I was looking at the script because I didn't notice that it returns a global variable rather than the value of the global) became superfluous once the arrays were implemented. Likewise, the function GetCampEnableMarker was reduced to a single line of code.

Considering the actual logics, there are two modifications to this script (3.2, 3.3) plus one carried over from the Caravan Leader script (3.1):

(3.1) Handling dead actors:
This is done in the new function UpdateAliases. The code is not self-explaining, so let me add a few comments:

All reference aliases (leaders and followers) are kept in the CaravanMembers array, which mimics a two-dimensional array and, therefore, is accessed by two indices (to simulate an n-dimensional array with one-dimensional arrays, a single appropriately designed array that is accessed by n indices is often more efficient than n separate arrays): WhichCaravan (running from 0 to 2; 0 - caravan A, 1 - caravan B, 2 - caravan C) and WhichMember (running from 0 to 3; 0 - leader, 1 - follower1, 2 - follower2, 3 - follower3; I have left out follower4 since it's nowhere used in the game). Leader aliases are at array positions 0-2, follower1 aliases at positions 3-5, follower2 aliases at positions 6-8 and follower3 aliases at positions 9-11. Thus, the reference alias defined by the two indices is at array position (3 * WhichMember + WhichCaravan). Also note that the array contains the actual variables (i.e. the reference aliases) rather than their values (i.e. the references they are pointing at) which makes manipulating them very easy.

The way this function works makes sure that the reference aliases are filled with living actors from the top of the following hierarchy: leader (WhichMember = 0) > follower1 (WhichMember = 1) > follower2 (WhichMember = 2) > follower3 (WhichMember = 3). That is, if the WhichMember alias has to be updated (triggered by the death of the respective actor), there are only two checks needed:

(a) check whether WhichMember < 3 (everyone except follower3)
(b) check whether the (WhichMember + 1) alias points to a valid reference (anything except none)

- If (a) is false, we're dealing with follower3: its reference alias will be cleared because there's no one left further down the hierarchy to replace him.
- The same holds if (a) is true and (b) is false: no one's left to replace the respective actor. If we're not dealing with the leader (WhichMember = 0), the reference alias is cleared as well (this is because the leaders keep their "not optional" status, as explained previously; it doesn't matter whether the leader's reference alias is now pointing at a dead actor because there's nobody still alive in the respective caravan when this happens).
- If (a) and (b) are true, we replace the dead actor by forcing the reference alias it was using to point at the actor the (WhichMember + 1) alias is using and then clear the latter. Now, we have to apply the same procedure to the (WhichMember + 1) alias, and so on, if necessary, until the end of the hierarchy. This is very simply done by calling the function from within itself (this is called a "recursion"; in case you didn't already know: the papyrus compiler can handle them), but with (WhichMember + 1) as the new WhichMember parameter.

(3.2) Bug 1 (potential bug):
In order to prevent the camp from suddenly popping up out of or disappearing into nowhere, the script has to make sure that the player isn't around when the camp is enabled/disabled. To do this, the original script checks whether the 3D model of the caravan leader is loaded as this is only true when the player is within the same cell or a neighbouring cell. In this case, the code stays in a while loop until the model unloads, which indicates that the player isn't around anymore, and only then proceeds to toggle the camp.

However, there are cases when the player isn't around and the 3D model still loaded, which prevent the camp from being toggled and these may be the reason for some of the incidents where caravans settle but camps are missing. For example, if the player is busy in an area just before a caravan arrives, the 3D model of the leader will be loaded before he actually reaches the camp site and the enable marker won't be toggled. I wonder whether people who have increased the UGridsToLoad value in the Skyrim.ini file do see this bug more often, as there should be a direct relationship.

Therefore, I have replaced this check by a different one which detects whether the camp is within the player's field of view by calculating his distance and view direction. This is done by the GetPlayerIsWatching function which returns "true" when the player's position is within a minimum distance from the camp site and/or his view direction is within a +/- 90° range from the direct line of sight. Of course, this function requires more resources than the check in the original script, but it doesn't run very often. It's called in a loop only when the player is standing near the camp site and watches, meaning that he's inactive so it really doesn't matter.

(3.3) Bug 2 (THE bug):
This is the most likely reason for most of the glitches occurring in un-modded games. In order to understand what's causing it, we have to look at the Update Caravan function, which toggles the camps and sets the parameters for the caravans to camp or move on to the next camp site. To make them behave properly, it is important that the function is only called at specific times:

(a) When the carvan arrives at a camp site:
This is done from the caravan leaders' travel AI packages: the function is called every time a travel package has completed (i.e. when a caravan has arrived at a new camp site).
(b) When the caravan leaves from a camp site:
This is done (or better: this should be done, see below) by registering the caravan leader for a single update after the camp site has been enabled (i.e. when the caravan starts camping). The update event is set to trigger after the preset camp time (24 hours) has passed (i.e. when the caravan is ready to leave) and this event is handled by the Caravan Leader script by calling the UpdateCaravan function.

However, instead of registering the leader for an update only after a camp has been enabled, it always registers him, but it does so twice. Since the globals that control the caravan behaviour (their values define which AI package will be selected) are always switched between 0 and 1 (the two camp states are represented by 0 and 1, as are the two camp sites), the second superfluous function call will cancel the unwanted changes triggered by the first superfluous call and there will be no glitch occurring when the second call occurs immediately after the second call (i.e. when the time between the calls
is too small to result in any visible misbehaviour before the second call resets the wrong parameters). In fact, the second call will follow the first one immediately in most of the cases - except when enabling the camp is delayed because the player is around: then, the second call will be delayed by the same time and the glitch occurs.

Fortunately, this was an easy fix.

Arthmoor said:
 
All seems to be working now, calling this fixed as of USKP 1.2.5.

Arthmoor said:
 
(In reply to comment #16)
> Created attachment 143 [details]
> Modified Caravan Script v1.2 (plain text format)
>
> Unlike previously noted, the CaravanLeader script can remain unaltered;
> modifications are restricted to the Caravan main script. As it has been
> realized now, the initialization should be retroactive. As a result, the
> OnInit event became superfluous and has been removed.

So should I completely toss out the modified CaravanLeaderScript.psc file then?

Sclerocephalus said:
 
Oops .. no!

This was meant to explain why I was not providing a modified CaravanLeader script, although I said so previously. But you're right, I did not make myself clear. Just forget the comments 16 & 17. In the future, I will keep from posting unreasoned stuff when I'm running low on caffeine.

Arthmoor said:
 
That's why I was asking :)

I hadn't removed anything yet, just applied the new update.

Sclerocephalus said:
 
(In reply to comment #10)
(1) "Marking this one as in progress [...]".

Yes. That's what I would strongly recommend in the present phase of testing.

(2) "Hopefully the game goes along with this retroactively too."

Well, that depends on whether the original script was running during the last save before the USKP has been installed. Since the script needs a short time for a single run and is called in relatively long intervals, there are good chances for both scenarios to occur and I expect that some users will find that the changes are retroactive, while others may find that they're not.

This is because a save takes precedence over every running script. That is, any running script can be interrupted at any point when the player saves his game. When this happens, all information needed to resume the script upon reload will be stored in the save game, including the stack PLUS a copy of the script itself (sic!). The save game is configured so as to resume all interrupted scripts upon reload and these tasks won't be removed from the task list until they finish properly. If they fail, they will be carried over as uncompleted tasks to every subsequent save (sic!). Also, as long as there is an old script version loaded from the save, the game won't use the updated version, at least not for any instances of the objects it was running on when it was interrupted (you can easily check this by letting your scripts print their version numbers on the log). For example, when your mod attaches scripts to objects, and an updated version attaches those scripts to additional objects, the latter objects will use the new script version, while most of the objects that were already configured in the earlier version will use the old script version loaded from the save. This may lead to additional problems, though, as there are now two scripts running which handle the same task, but are not doing it in exactly the same way. Similar problems occur when you choose a different name for the script upgrade (which, therefore, isn't an appropriate solution either).

Storing the script in the save actually makes sense as this was clearly intended to prevent errors from occurring that are related to script modifications between a save and the subsequent reload. Unfortunately though, if the script handles mod-added objects, which are removed with the esp/esm files, this practice leads to failures. For the same reason, the practice hitherto known as "clean save procedure" is entirely useless with Skyrim, as it does by no means produce a clean save, but makes things even worse in that it removes all mod-added content and only makes certain that all mod-related scripts loaded from a save will fail for sure. Those failures usually plaster the log with a salvo of error messages, and this repeats every time this save or any subsequent saves are loaded, until the interrupted script tasks eventually succeed to finish properly. In general, however, this is never the case, because both users and authors of scripted mods apparently are not aware of this problem and do not usually take any precautions to purge their saves from script debris before they uninstall or upgrade such mods. Thus, any uninstallation or upgrade of a scripted mod will add a bunch of new tasks to the save that will never complete, produce another long list of error messages in the log and bloat the saves. Also, weird glitches will occur more frequently (it somehow seems that the engine skips other tasks when it is too busy with spitting out error messages) and, sooner or later, the game becomes unplayable. At this point, starting a new game is usually the only cure.

A quest, therefore, is not likely to use a new script version unless it is reset (sic!). In order to apply the changes retroactively, a line to reset the caravans quest has to be added to the USKP's OnStart script (the mod has its own OnStart quest, so I assume that there's also an OnStart script associated with it). Since the caravans quest is basically an inifnite loop of predictable events, resetting it won't break the game (some of the very early dialogue may become available again, but this should be an acceptable nuisance). It shouldn't even affect Kharjo (in case you hired him or made him a blade), because his hireling and blade aliases will override the caravans alias (from which he was never cleared in the original script anyway).

Since the USKP now does provide more and more script fixes, its conception may need to be reconsidered with respect to the scripted mod problem, so please read on.

I have been struggling with this problem for quite some time now, since it was really annoying to start a new game every time I had to test a fix or improvement to one of my own scripts (First law of experimental science: Never vary more than one parameter at once if you want to draw any valid conclusions from your experimental results) and eventually took some time to conceive a solution.

The procedure is as follows: every time an upgrade has been installed successfully, I prepare an esp with several slight modifications:

(a) All scripts that are attached to objects and triggered by events other than activation by the player are detached from those objects in the modified esp. As everything else remains unchanged, incomplete tasks stored in the save can complete without problem, but none of the scripts will be called again and, after some time, no information related to these scripts will be stored when the game is saved (the success of this procedure can be easily observed in the log: as expected, no script-related messages appear when this save is reloaded).
(b) All other scripts running in the background permanently or intermittently (e.g. the quest scripts) are written with a built-in stop function from the beginning. This function is called with priority when an external global variable (the terminator variable, so to say) has a certain value. The global is set to the respective value only in the modified esp, so the stop function will not run in regular conditions (the function has to be always present in the script, though, because the scripts must not be modified until all script-related data has been safely purged from the save file). The stop function will stop the quest (this will also clear all variables owned by scripts attached to this quest) and, if necessary, survey the completion of certain tasks that will not stop running immediately. When this is done, the function displays a message on the screen, which tells me that I can now safely remove or upgrade my mod.

The next time I have to upgrade the mod, I save the game, then overwrite the esp with the modified esp (see above, NOT the upgraded esp; also do NOT perform a clean save here), reload the saved game and wait until the message displays. When the game is saved now, all the script debris is gone. Now, the esp can be safely deleted (to uninstall the mod) or replaced by the upgraded esp.

The only obvious disadvantage of this procedure is that the player has to run the modified esp for a certain real time (the game's wait function will not speed up things as it doesn't affect the real time) during which he/she can do nothing but wait (because there may be some glitches while all mod-related scripts are more or less abruptly stopped).

(3) Did you get the latest version of my script ? It accidentally ended up here: bug 198.

Arthmoor said:
 
Not to sound dismissive or something, but I understand all too well the pitfalls of Papyrus and what it's doing to the saves. The comment was more just me hoping it would play nice rather than not understanding that it might not.

As for your other procedure, no, that's just not practical for something like the USKP. It's hard enough getting users to take it up as it is, adding a layer of complexity like that just to make a futile attempt at forcibly purging script info is never going to fly.

We're stuck with what we have, unfortunately. While I do understand why they did things this way, that doesn't mean we have to like the implementation. Especially when you can't ever get rid of missing script data from a save no matter how long the mod has been absent from it.

Arthmoor said:
 
You had a fix for that script at one point in time, right? Would you mind attaching the source file for that here? Any little bits to help get this thing under control would be appreciated.

Sclerocephalus said:
 
Yes, my procedure is horribly impractical, and it also won't work for mods that have been absent for some time. It is not futile, though, when it is applied immediately.

Thus, your post was an almost perfect occasion to give a short summary of the problem and to clarify that there are no simple solutions. This could help to educate some people browsing the log, who are less informed than we are and probably won't understand why some bugs can't be fixed retroactively and why fixing script bugs remains problematic (perhaps some of them will stop asking now for the ever same fixes although they were told it's not possible; to quote Benjamin Franklin: "Tell me and I'll listen; Show me and I'll look; Involve me and I'll learn" - when I give seminars twice a year, this always works surprisingly well, even with difficult characters).

Another intention was to point mod authors to a possible solution. Once the modified esp is available, the procedure is not that difficult to understand (save - overwrite esp - reload and wait for message - save again). Hence, if the author of a scripted mod would provide this esp with his mod, most of the users should be able to safely upgrade/uninstall at least that mod. If I ever manage to get the project finished I'm presently working on, I will try this and see what happens. Perhaps, others will follow this example and in the end, the world of modded Skyrim games could be made at least a little bit safer.

Sclerocephalus said:
 
I just had a look at the papyrus log posted by alt3rn1ty here:
http://forums.bethsoft.com/topic/1429096-relzwipz-unofficial-skyrim-patch-thread-19/page__st__90

This indicates that his engine runs the new script, though it doesn't work because the OnInit event was never run (it would only trigger at quest start) and the alias array remains empty.

There is a simple workaround: any alias which is registered for an update can't be empty, so the only thing I have to add is a check to the CaravanLeader script whether his field in the alias array is empty and, if so, call the initialization as a function (this will be only called once, since the condition will be never fulfilled again for any leader after it has run).

Will work on it now and add the updated scripts later.

Sclerocephalus said:
 
Unlike previously noted, the CaravanLeader script can remain unaltered; modifications are restricted to the Caravan main script. As it has been realized now, the initialization should be retroactive. As a result, the OnInit event became superfluous and has been removed.

Sclerocephalus said:
 
This is the right file. Forget the previous one.

Sclerocephalus said:
 
The mysterious Caravan script again ...

Ahkari's behaviour indicates that she was released from the Caravan quest, since wandering around at the editor location is her actor AI package. If she was still aliased, she would sit at the editor location with crossed legs (this is her only sandbox package while engaged in the quest, and the quest package would override her actor package). Also, the rest of the party would not have moved on, since the followers' travel packages consist in following their leader. Obiously thus, she was replaced as the caravan leader by another actor.

If the script would work as intended, this would never have happened: actors will not get replaced nor released unless they're dead (yes, the caravan guys can die - while their faction has been carefully set up so as to prevent them from being involved in a combat, the actors themselves are neither protected nor essential). Though, it appears that the script which handles this (the OnDeath event of the CaravanLeaderScript) is not sufficiently multi-threading safe. This is the only plausible explanation I can offer.

Hence, if two or more caravan members die at once and several instances of the OnDeath script are running at the same time, weird results have to be expected. In vanilla Skyrim, this probably requires a dragon attack, but in a modded game, this is much more likely to occur as it only requires a common-or-garden heave-ho mod which adds a bunch of killers without thoughtfully considering faction relationships or otherwise uses enemies the NPCs are not set up to deal with (there's at least one popular mod out there that places a horde of draugrs in the wilderness ...).

Sclerocephalus said:
 
Update 2:

I have attached an updated version with several improvements:

(1) I noticed that there are two built-in papyrus functions which make much of the code in the GetPlayerIsWatching function superfluous, and I have modified the script accordingly. When I learned programming in the early 1980s, the languages were not nearly as versatile.

(2) After some testing, I don't see a reason now why the caravan leader aliases cannot be set up as optional. In fact, doing so will further simplify matters as it eliminates the need for separate handling of leader and follower aliases in the OnDeath event (i.e. the UpdateAliases function).

One might argue that spending that much time on the OnDeath handling is a waste of time, because the caravan guys almost never die in an unmodded game. This is because the individual actors filling the aliases have been set up as "protected". Though there's obviously still a finite probability for this to happen, as otherwise there would have been no OnDeath event in the original Caravan Leader script. Thus, better safe than not.

Another point to consider is that the USKP has added clean-up scripts to all actors filling the caravan aliases and I suspect that this will result in crashes because the aliases are not set up to accept disabled actors. I strongly recommend therefore to set up the aliases as "optional" and let my script clear them once an actor is dead (once the actor has been released from the alias, it won't matter whether he's enabled or disabled). Alternatively, you may tick the "allow disabled" option to make them compatible with the clean-up script. Note, however, that my script will not work when the aliases are "not optional".

Sclerocephalus said:
 
Update 2:

I have attached an updated version with several improvements:

(1) I noticed that there are two built-in papyrus functions which make much of the code in the GetPlayerIsWatching function superfluous, and I have modified the script accordingly. When I learned programming in the early 1980s, the languages were not nearly as versatile.

(2) After some testing, I don't see a reason now why the caravan leader aliases cannot be set up as optional. In fact, doing so will further simplify matters as it eliminates the need for separate handling of leader and follower aliases in the OnDeath event (i.e. the UpdateAliases function).

One might argue that spending that much time on the OnDeath handling is a waste of time, because the caravan guys almost never die in an unmodded game. This is because the individual actors filling the aliases have been set up as "protected". Though there's obviously still a finite probability for this to happen, as otherwise there would have been no OnDeath event in the original Caravan Leader script. Thus, better safe than not.

Another point to consider is that the USKP has added clean-up scripts to all actors filling the caravan aliases and I suspect that this will result in crashes because the aliases are not set up to accept disabled actors. I strongly recommend therefore to set up the aliases as "optional" and let my script clear them once an actor is dead (once the actor has been released from the alias, it won't matter whether he's enabled or disabled). Alternatively, you may tick the "allow disabled" option to make them compatible with the clean-up script. Note, however, that my script will not work when the aliases are "not optional".


[Edit]
I see that, for some obscure reason, the attachment has been erroneously uploaded to bug 198 (the next one in the list) instead of this one. Sorry for the inconvenience.

Sclerocephalus said:
 
Update:

It's probably wise to unregister the caravan leaders for updates once they are dead and nobody's left to replace them. To do so, the UpdateAliases function has to be slightly modified. Its new version should look as follows:

******************************************************************************

Function UpdateAliases (Int WhichCaravan, Int WhichMember)

Int MemberToReplaceWith = WhichMember + 1

If (WhichMember < 3)

If CaravanMembers [WhichCaravan + 3 * MemberToReplaceWith]
CaravanMembers [WhichCaravan + 3 * WhichMember].ForceRefTo (CaravanMembers [WhichCaravan + 3 * MemberToReplaceWith].GetReference())
CaravanMembers [WhichCaravan + 3 * MemberToReplaceWith].Clear()

UpdateAliases (WhichCaravan, MemberToReplaceWith)

ElseIf (WhichMember != 0)
CaravanMembers [WhichCaravan + 3 * WhichMember].Clear()
ElseIf (WhichMember == 0)
CaravanMembers [WhichCaravan].UnregisterForUpdateGameTime()
EndIf

Else
CaravanMembers [WhichCaravan + 3 * WhichMember].Clear()
EndIf

EndFunction

*******************************************************************************

Arthmoor said:
 
Are there any ESP changes needed for this to work?

Sclerocephalus said:
 
Yes.

- (1) Flag the caravans quest's aliases as "optional".
- (2) Attach the CaravanFollowerScript (new, it's one of the scripts between the dotted lines in comment 3) to the follower aliases.
- (3) Replace the script fragments attached to the leaders' travel packages.

Arthmoor said:
 
OK, thanks. Marking this one as in progress since it will need more special attention that just assuming it's all entirely fixed.

Hopefully the game goes along with this retroactively too.

Showing Comments 1 - 19 of 19