AniMisc
Previous Next


Morrowind modding



I'm not really a Morrowind modder. Morrowind modders are like hackers, only for Morrowind and its Construction Set. I'm intimidated by their highly technical discussions on how to produce a certain effect that should be easy to produce, only the gamemakers, having made an RPG, thought that fans would want to create RPG-type mods with a few monsters to kill, some treasure to snatch, perhaps some dialogue and text for the diary. What fans in fact want to create is a complete personalized virtual playground using the Morrowind engine. It's a bit like how Maxis underestimated the kind of customization that Sims fans wanted to make. Morrowind is slightly worse: firstly, not all syntactical features are covered in the helpfile, and some are broken. Secondly, conditions that should be easy to test for, are not. Take swimming. When a character is deep enough in water, the wading animation changes to a swimmming animation. So the game "knows" the character is in water. Now I need a script that asks "is the character swimming or not" because I've added a bottle that can be filled with water, but only when there is water near, and water is certainly very near when you're up to your neck in it. Well, even though the game "knows", there is no way of asking it. The only, very roundabout, way of knowing if your game character is swimming, as I learned on a forum discussion concerning a bathing mod, is to ask the game whether any of the splish-splash swimming sounds are being played. For your own character, that is. I don't know how I would go about testing this for a companion character.

What I am is, in all senses of the word, an amateur. The game booklet said I could add and change things, so I did. Or rather, I tried to. I made a relatively easy mod to set things right concerning a certain Fargoth, and embarked on a much more difficult mod to teach the arrogant Dunmer (or rather, one arrogant Dunmer) a lesson in humility, which I soon became stuck on. I don't even know if I'll ever finish it. But in the course of doing so, I educated myself on scripting, mostly through version 9 of the fan-written Morrowind Scripting for Dummies and discovered a few things, not all of them mentioned in this document. So, for anyone surfing on the quirks of Morrowind modding, I thought I'd share.

Undocumented keys

Not to do with modding itself, but handy when testing mods. Two keyboard shortcuts I found on Morrowind sites while surfing: if you want to take something out of your inventory of which you have more than one, for instance: 9 Cure Disease potions, holding down the Ctrl key while clicking on the item will pick up only one item while skipping the usual question of how much to take, while holding down the Shift key picks up the whole lot without question. I also read that as long as you hold down the Ctrl key, the number of times you click on the item will be the number of items picked up, but that didn't quite work for me. Not undocumented, but simply a default keybinding that I discovered by accident before I saw that it can be changed in the game options: pressing Q once is the same as keeping W pressed down: the character keeps walking. Press W, or Q again, to stop the walking. Opening a menu temporarily stops the walking. Q also works with Shift (run) and Ctrl (sneak). Using Q and Capslock saves wear on the keyboard.

Crash caused by battery

When the game has a CTD (Crash To Desktop) this can have a number of causes, like script errors and trying to delete an object that is inside someone's inventory. Or the laptop battery may be almost empty. I work on a laptop and, to conserve the battery, let it run empty before recharging it, instead of having the laptop plugged in all the time. While playing, I can't see the state of the battery, and I tend to forget the time, so often either the computer announces that it will go into hibernation - and when waking it up, the game will crash after a while - or the screen just goes black and when I have my desktop back, I see the battery icon with a red cross through it. Sudden unexplained script errors can also be a matter of the battery running empty. A CTD can also occur just before an extra bit of animation is about to occur, notably the player dying in a fight with many creatures and/or NPCs, even when the battery is not almost empty. It seems that whenever the graphics card doesn't get the power it needs, the game shuts down.

When a dialogue screen (especially the character creation dialogue) is opened while the computer goes into hibernation, the game may, after a restart, continue without problems. When a book or journal screen is open, it will crash. I've replaced the "low battery" popup with a jarring system sound (can be heard even through the background music) to reconnect the laptop to a power source on time and not lose all unsaved changes due to the computer going into hibernation.

TESCS, Morrowind and GOTY

The TESCS that comes with the original Morrowind does not ask to save a changed plugin before reloading. The TESCS with Morrowind GOTY fortunately does.

The GOTY TESCS also lets me indicate which journal line is the quest title, and which line is the quest ending. This is for the ordered display of quests which was so sorely lacking in the original, and was introduced in its expansions. The space used for these indications is space left blank in a Morrowind-only journal, so a plugin with journals created in the GOTY TESCS can probably be safely used in plain Morrowind, providing that the plugin does not contain any Evil GMSTs (expansion game settings) or startscripts.

Here's something that both TESCS's do: I add a lot of dialogue for Fargoth, about half of it also for the cell "Seyda Neen, Fargoth's house" since it has to be said in the privacy of his own house. I filter all dialogue for "fargoth" to check it. The dialogue that is also connected to a place, like Fargoth's house, does not show up. That's strange, because otherwise I get all Fargoth-related dialogue, not only what Fargoth personally says, but also lines for anyone of his race and/or faction.

I've read that the different TES versions compile animations differently because the animations are coded in a different way internally not only between Morrowind and Tribunal, but also between patched versions of the same game.

Syntax, and lazy syntax

Syntax is putting inverted comma's around all object IDs, using a comma after each numerical argument, using capitals to mark the beginnings of words (ie. "StartScript" instead of "startscript") and starting and ending each script with "Begin Scriptname" and "End Scriptname". Lazy syntax is using inverted comma's only around IDs with spaces or apostrophes in them, skipping the comma and the capitals, and ending a script simply with "end". There is an argument for using lazy syntax: less chances of making mistakes. The comma thing, for instance:

Khinjarsi->PositionCell -172 -506 263 245 "Suran, Desele's House of Earthly Delights"

is the same as:

"Khinjarsi"->PositionCell, -172, -506, 263, 245, "Suran, Desele's House of Earthly Delights"

just less typing, and no errors just because a comma was forgot or typed double. And the cryptic errors (the script sometimes ran and sometimes didn't) caused by a script ending on "End Begin Scriptname" (I'd copied and pasted the starting line to the ending line) make a case for always ending a script with just "End". For legibility, I do use capitals as in "StartScript" and "doOnce", but not of course in "if" and "endif".

Script says it doesn't exist

If you create a new script, save it once before putting a StopScript command with its own name in it. Alternatively, create your MyNewScript with "StopScript MyNewScript" somewhere near the bottom, "save" (ie. compile) it once, get an error message that said script does not exist, save again, all is well. Because of the incomplete way scripts are compiled, they should, for safety's sake, always be saved twice.

I've found a second and even more amusing error. I create a new spell and, in the same editing session, add "AddSpell spell-name" to an existing script. Before saving (ie. compiling) the script, I change the spell's name in the spell editor (open spell, change ID, choose No to question "create new object"). Then, I try to save the script. It claims the spell doesn't exist! Because I hadn't compiled the script for the old spellname, I don't know whether the step of changing the spell ID was even necessary; apparently if you open a script window and then start adding spells and other objects, the script compiler won't know about the new objects added since the script window was opened. The only solution was saving the whole data file (with the script window still open, so I don't lose the script changes) and then reloading the whole file. Then I opened and saved the script again, and it compiled.

Missing persons and scripts

An object, whether NPC or creature or anything else, cannot be referred to in a script unless it has been put in the game with the game editor and is loaded when the game starts, as anyone who makes a script to adjust the AI settings of Ranes Ienith can tell. The TESCS shows that there are 0 references of him in the game, and he is only dropped into it with a PlaceAtPC when his brother is attacked. The only way to refer to them in a script is to place them in the game and then find a way to make them disappear until they are needed. From Tribunal onwards, they can be disabled in a user-made start script. In a Morrowind-only installation, which runs only the game's own startup script, statics and containers are best disabled by their own script, items are best inserted on-the-fly with the PlaceAtPC method, and unique NPCs, although they can clearly be inserted with the PlaceAtPC method, are best put in some hidden cell and then teleported to the right spot from a global script or dialogue box result field (although PositionCell used in the result field is said to cause crashes).

Something else that can't be referred to in scripts: new instances of persons. The first instance of a NPC, say Fargoth, may appear to be "fargoth" but is in fact "fargoth0000" (I don't know exactly how many zeros, it might be up to 8). If I create a new Fargoth in the console or in a script, that will be "fargoth0001". The next one will be number 2. Andsoforth. And these instances cannot be separately addressed in a script. I cannot, for instance, kill off both copies and keep only the original. Which is understandable since, like Ranes Ienith, these copies have not been placed in the game by the editor. It might be that a script can refer to instances that are placed in-game with the editor, like the many guard clones.

Missing objects and scripts

If an object ID referred to in a script doesn't exist or hasn't been placed in the game (or was made after the script was opened for editing, see Script says it doesn't exist) then of course the script won't compile. If the object is referred to in the little script known as the result field, I can only compile it with "Check error results", which, instead of giving an error message, shuts down TESCS. Fortunately, I had saved the file before running the check and so didn't lose any changes.

In this case, I used GetDistance on Aurane Frernis's three alchemical formulas. The first lies on a table/counter. The second is in a chest. The third is in her personal inventory. The second and third don't count as "placed in the game" since they are only in an inventory, therefore, I can't use GetDistance on them in a script.

"Error check results" changes global variables

It is possible to set the value of global variables in the dialogue window's result field. When the TESCS dialogue "Error check results" is run, all code in the result fields is carried out, possibly changing the global variables; if so, they are not set back to their initial value and the plugin can be saved with a global having a starting value of, say, 5, which can produce strange results for scripts and replies that depend on this global variable. The solution is to i. always check global variables and manually reset to 0 if needed before saving (don't alter any standard globals!) and ii. save before "Error check results", then run the check, then reload the plugin without saving.

MessageBox

The results field is the box under the dialogue response used for comments and scripted commands, like MessageBox. The syntax for the text-displaying command MessageBox is:

MessageBox "all the text you like"

or:

MessageBox "text text text %g text %g" variable1 variable2

where %g indicates each numerical variable to be inserted in the displayed string. Comments, on the other hand, are lines, or the end parts of lines, that begin with a semicolon:

; comment beginning at start of line

or:

set GlobalVarName to 2 ; comment to say that a global var is set to 2

The whole MessageBox line, ie. the command, variables and everything between quotes must not exceed 512 bytes. If it is longer, and in a results field, and you run "Error check results", a MessageBox line that is too long causes a loop which can only be left by shutting down the TESCS with Ctrl-Alt-Delete; another reason to save the plugin before running "Error check results".

Since a semicolon is always seen as the start of a comment, even in the quotes-enclosed text of a MessageBox line, using a semicolon in the MessageBox text causes a "quotes mismatch" error.

The variables that can be displayed are: in a script, global variables or local variables from the script itself; in a result field, global variables or local variables in the script of the NPC/creature addressed. In a result field, it is not possible to declare a local variable, and no local variables from other scripts can be displayed. In a script, I tried to display local variables from another script in the following way:

MessageBox "INB level %g willp %g" "npcname".inblevel "npcname".inbwillpower

but presumably because of the quotes, the variables were seen as buttons. So in the script with the MessageBox, I declared a variable "helpval1" and "helpval2", filled these with the values of "npcname", and used them in the MessageBox line.

Standard display variables

There are a number of standard variables like %Name, %Class, %Race, %PCName, %PCRace. Using these in dialogue will see them replaced with the variable's value in the game. So "I am %Name, %Class" will appear in the game as "I am Yokel, Commoner" or "I am Fatcat, Merchant" depending on who says it. Now, "dialogue", ie. displayed text, can also be put in the result field, using "MessageBox" and the text string; the text will be displayed in a different colour and the same variables can be used, but with a caret instead of a percent sign: ^Name, ^Class, ^Race, ^PCName, ^PCRace. I haven't tested them all, but for ^PCRace, this does not work. No race was displayed for my PC, just "^PCRace". Now this may have been because I didn't use the variable with MessageBox, but with Choice, another command that displays text from the result field.

As said in Morrowind Scripting for Dummies, ^Name will just display the value of ^PCName, so in result fields the name of the addressed NPC should be used, while in a script a paraphrase is needed ("your companion", "your slave"). ^Cell applies only to the cell the player is in, and can't be used to locate companions gone missing. Since it is strictly for display, neither can it be used in the following way:

"npcname"->PositionCell 0 0 0 0 "^Cell"

Too bad, eh.

Dialogue strings: allow yes to all

When opening any plugin in the GOTY TESCS, be ready for many error messages about preceding and following strings being different. And when error-checking dialogue, expect a lot of errors too. For the first, a setting is needed which is not in the Morrowind.ini, so this whole line has to be added:

AllowYesToAll=1

If you get text string errors on loading in the TESCS, a third "cancel" button is added to the "yes/no" buttons, and clicking on this button skips the other error messages.

For the second, the only real errors are the name of Fons Beren's journal and the nonexistent stock certificate journal, which ought to be a global variable. The other "errors" disappeared after I'd added a lot of code to result fields of my own.

Doors and their instances

Just as "Guard->SetHealth" may not do anything with multiple references of the NPC "Guard" in the game, so the inevitable multiple instances of the same door are hard to influence just using the door's name. But doors seem to be worse than NPCs in this respect.

If (discovered in the Suran Slave Market) four instances of the same basic door all have "opened by key X" in their reference data, then key X will only open the first of these doors. Even if I set the instances to be opened by four different keys X, X1, X2 and X3, only one these keys will open only the first door. On the other hand, the same key can be used for different doors with different IDs, but if used for different doors with the same ID, it only works for the first instance of that door ID. So three of the four doors had to be replaced by doors with unique IDs.

A door can't be (un)locked with an external script. If I use "lock door_x 50" in a result field, the game crashes; if I use it in a global or local script, the script aborts, and most or all of the code in it is not executed. (A Morrowind-only installation actually displays a message at startup that "reference door_x not found", while GOTY loads silently and the game either crashes or the script aborts silently, too.) A door can only be safely (un)locked from its own local script, so, to remotely (un)lock it, I let its script check for a global variable or journal entry.

Health and death

For testing purposes, I sometimes quickly kill off a NPC by opening the console and typing "npcname->SetHealth 0", or selecting the NPC by clicking on it with the console open, and typing "SetHealth 0". In the second case, "SetHealth 1" will also do the trick. It is not possible to kill NPCs with "npcname->SetHealth 0" if they aren't in that cell and haven't been loaded by the game yet (npcname->ForceGreeting won't work either, although npcname->PositionCell will) because nothing will change; in that situation a script using GetHealth will act as if the NPC's health is 0, so it should use GetDeadCount for a more accurate reaction to the situation.

It is also not possible to remotely kill NPCs from a script with "npcname->SetHealth 0". If they are in the same cell or already loaded, their health will at least be set to 0. If they are in the same cell as you, they will even go through the motions of dying and become corpses. But their "dead count" will not be augmented, so if you use the "Dead" condition in dialogue, you won't get the right dialogue responses. I've tried this with an NPC whose health was set to 0 before he was disabled: even teleporting him to the cell where the PC was for his little death-grunt before disabling him didn't make him "dead" to others. So instead of the "Dead" condition, I used a "he's dead, folks," journal entry to get the right responses.

Odd things when resting and levitating

It should be obvious: you can't rest and levitate (or rest and swim) at the same time. So if you ask for a blessing at the Shrine of Daring, which confers levitation for a long period, you will not be able to sleep until the blessing wears off. You can't even use T to wait and make it wear off more quickly. However, you can also lose companions. When teleporting by opening a door to another cell, or by making use of travel services, if the PC is levitating, any following NPC is left behind. The "Decius and Vorwoda" plugin addresses the door issue by replacing all doors in the game with "companion-friendly" doors which have to be clicked on twice. Without this feature, testing a mod can be a headache as you wonder where that companion went. Another solution is to have the companion stop following when you start levitating.

When using T to wait in jail, I found that jail doors (or any non-teleporting doors?) are not good at keeping NPCs confined. I would rest for 24 hours before a prison cell door and find an Ordinator behind bars, while the jailbird I'd been watching was somewhere up the corridor. When putting NPCs behind bars, it's best to set their AIWander distance to 0.

PositionCell: observations

The best way to put a NPC somewhere is PositionCell. Unlike GetPos/SetPos, which set the coordinates within the cell the (N)PC is in, PositionCell states not only the coordinates but also the facing angle and the cell to which these arguments apply, all in one line. There is one disadvantage: unlike SetPos, PositionCell doesn't take variables. From Tribunal and upwards both are said to accept float variables, which must be local and not global variables, but when I transported Desele's dancing girls upstairs and back, storing their original positions in variables to use for their return, they always ended up near the door, which, I later found, corresponded to the coordinates 0, 0, 0. Not even SetAtStart could return them to their original positions, so I looked up their original coordinates and used these values with PositionCell in the teleporting script. Maybe, for PositionCell, "local variable" means it has to be a variable declared in the script of the NPC, rather than a local variable in a global script? (This has been tested by altering the slavescript and DOES NOT WORK.)

PositionCell will work in cells that haven't been visited yet, and for NPCs that haven't been loaded yet - as opposed to SetPos, which only works if the NPC is loaded and in an active cell (preferably the cell the PC is in, I don't know whether there is a difference between interior and exterior cells). There is a potential problem with this. If a NPC is teleported to a cell the PC has yet to visit, the PC will enter the cell, the NPC will be loaded along with the cell, and all will be fine. But if a not-yet-loaded NPC is teleported to a cell where the PC is already standing, and this NPC has a script, the script will not run properly, because the NPC has been placed in the cell, but not loaded along with it. And if this is the famous NoLore-script or another script to do with dialogue, then if the PC addresses the NPC or vice versa, the game will crash. I had this problem when teleporting a few NPCs including Fons Beren and m'Aiq to Desele's House of Earthly Delights; they either showed up as black shadows, or looked normal but caused a CTD when greeted. Disabling and re-enabling the NPC sadly doesn't fix this. The two possible solutions are: have the NPC teleported to some hidden corner of the cell the PC is about to enter and brought out as necessary, or, but this is rather ugly, teleport the PC to the NPC's cell and both of them back to the cell where the NPC is supposed to appear. Blacking out the screen with FadeOut or a spell of blindness during this jump back and forth did not work.

Using PositionCell from the result field instead of a script is supposed to crash the game. I wonder if this has anything to do with the problem above, because I haven't had crashes as long as I don't teleport "new" NPCs into the PC's cell.

That I can use PositionCell on unloaded NPCs in faraway cells sometimes makes me forget what I can't do. I had three slaves teleported from Clutter Warehouse to an island on the Bitter Coast, all three paralyzed by adding the disease Witchwither to their inventories, and one hurt by using SetHealth -20. Unfortunately, neither AddSpell nor SetHealth had an effect, and I had to use AIWander settings (something else that does work on unloaded NPCs in faraway cells) to immobilize them. I then added code to the first greeting to add the disease and decrease health.

Using a nonexistent cell name with PositionCell lands the teleported character in the first existing cell, somewhere in the Ashlands: at coordinates 0, 0, 0 in cell 0, 0 to be precise.

Two quirks discovered while working on a companion: if, in an active cell, a script teleports an NPC towards, for instance, a shopkeeper, and adds shopping items to his inventory to the point of overburdening him, he snaps back to his old position. Teleporting and adding items in two different scripts would probably prevent that. If, again in an active cell, a script makes him cast a spell and then teleport, he casts the spell, but PositionCell doesn't happen, as if the spellcasting cancelled it. I tried making him cast a spell in the result field and then use a script to teleport him; this caused a CTD. Finally I put the spellcasting back in the script and made him wait 4 seconds (too short and the spellcasting cancels the teleporting, too long and he starts greeting the PC) before teleporting with PositionCell.

The fourth numerical argument for PositionCell is the Z angle, the direction in which the NPC faces. This is said not to work, but it does. This is because solely with PositionCell and NPCs, not degrees but minutes are used. One degree is 60 minutes, so the number in degrees has to be multiplied by 60, or the rotation is too small to notice. PositionCell for the PC uses degrees, and Get/SetAngle for both PC and NPC also uses degrees. But simply using "SetAngle Z 180" on a NPC will not make the NPC turn round and face a different direction.

There is a command "Face x y" but if I use this on a NPC newly placed close to the player, the NPC will start to circle the player. And since Position and PositionCell don't take variables, I can't use those to change a NPC's facing direction either. If the NPC is not following and the PC approaches, the NPC will automatically swing around to face the PC (even if the NPC is lying down).

Lastly, using PositionCell in the console to move a NPC standing next to me somewhere on the Bitter Coast to Balmora caused a CTD.

Armour quirks

GetArmorType won't work for NPCs, in either a script or the result field. Correction: I've found it will work in a script, if I put the value returned by GetArmorType in a (local) variable and use this variable where needed. It is not possible to use local variables in the result field, so here I would need to use global variables. I opted to run a script instead. That's how I found out that if the first GetArmorType call in the script is for the helmet ("GetArmortype 0") no value is returned, possibly because of the 0. If I start with "GetArmorType 1" or any value higher than 0, and then use "GetArmorType 0" to check for the helmet, it does work. I know that GetArmorType only works since Tribunal, but this was discovered in a GOTY installation with the latest patch applied.

Companions (NPCs in "follow" mode) are like an extension of the PC. They will not speak out when the PC commits a crime, and when they attack something, the PC bears the blame. (And gets the experience points?) But companions also somehow inherit the PC's armour skills. If I give a slave bracer to a companion to carry, he will not auto-equip it, as it is technically heavy armour, and his highest armour skill is Light Armor. But when I play as an Orc and have a high Heavy Armor skill, my companion, with his higher Light Armor skill, nevertheless equips the slave bracer. Perhaps useful information for modders who want to make a Morrowind-only companion, as Morrowind-only NPCs can't be told to equip something. Although this is a GOTY-requiring companion, so I can't test what he does under Morrowind alone.

Although a piece of armour has "health" just like a (N)PC, I can't "damage" a piece of armour with ModCurrentHealth, ModHealth or SetHealth, either as a command from a script or result field, or in a script attached to that item. Since the script would act directly on the reference, I deduce that these commands simply don't work for armour (and possibly weapons). I can only "damage" armour by placing it in the game with the editor, and then editing its reference data.

Straight from the result field

Code in the result field is executed immediately, while the dialogue menu is still open. If sounds are played, they are played straight away, all at the same time. SetPos or PositionCell are executed immediately, which may cause crashes if the object being teleported was doing something else. Objects are added to or removed from the inventory with AddItem and RemoveItem instantly, and Journal just as instantly adds journal entries.

This mainly matters for its effect on dialogue topics. I could take the commands from the result field and put them in a script called DoStuffNow, and put "StartScript DoStuffNow" in the result field instead. Now, any teleporting will not happen until the dialogue window is closed, and even though the dialogue will say "Your journal has been updated", if the journal update affects the available topics, the topics will also not be updated until I click on them or close and reopen the dialogue menu; a now obsolete topic will instead get a reply like "I don't trust you enough to tell you about that". I haven't tested topics that depend on items in the inventory, but conclude that any action which should update topics belongs in the result field, not in a script.

The PlaceAtPC method

Once, I needed a damaged helmet to materialize on a counter. There was no way to place and then damage it while the game was running, so I had to put it on the counter damaged in TESCS, disable it in a startup script, and enable it when needed.

Much later, I needed three books to appear in Thieves Guild halls as part of a quest, in a mod that should work in Morrowind only, so I couldn't add startup scripts. I tried to put them in the game and have their internal script disable them, then poll for a certain journal entry to enable them. Their polling took so much time that it slowed the game. It would be better not to create them unless/until needed, and the simplest way to put an object in the game is to either add it to someone's inventory, or use PlaceAtPC which creates an instance of it near the PC. But how to teleport them to the place where they're supposed to be? I decided to attach a script containing a one-time PositionCell command to the books. They teleported themselves so promptly that I didn't even have time to bend down and see if they were lying at my feet. So I decided that if I ever needed to pop an item into the game, I would use PlaceAtPC and let the item's script take it to where it was needed.

For the same plugin, I also needed three crates of moon sugar to appear on a dock. I tried the PlaceAtPC method again, but for some reason (containers don't like being teleported?) it didn't work. So I used the helmet trick again: pre-placed and disabled them, although, still not being able to do this in a startup script, I had to let them disable themselves and then enable themselves at the right time by polling for a journal entry again. For some reason, this worked well with the crates. Maybe it was because the crates were in a thinly populated exterior, while the books were in interior cells with plenty of NPCs.

The crates were less stable in Morrowind only than in the GOTY installation. When I placed them in the console, I got memory pointer errors and a message "Trying to Runfunction .. greater than index count". When I emptied them, which is the cue for their own scripts to make them disappear, they didn't, but when I left the cell and then revisited it later, they did disappear, but with an error message.

The lights method

A page with scripting tips related to Josh's Farmer Mod says that global variables sometimes update too slowly to be usable in scripts - which I've found to be true - and that another way to quickly test for a condition is to add lights (not candles or torches, but actual lights) to an inventory, as they can be tested for with GetItemCount, but won't show up in the inventory. I thought I would use this principle for a quest in which, depending on my actions, two groups of people would be behind bars for a specified number of days. A script would count the days and position them back in their old spot once they'd done their time. It couldn't be a global script because then it would not restart after saving and reloading. It couldn't be a local script because I didn't want to tamper with the NPCs' scripts. Besides, it had to keep running and counting as long as the PC was active. I hit on the idea of putting the script in a new kind of light and adding this light to the PC's inventory, where it would do its counting invisibly and then remove itself from the PC's inventory, not in its own script because that has been shown to cause a CTD, but from another little script that it would call. That's how I discovered two things: first, deleting an item with a script from one's inventory even through an external script will make the game crash, and second, the lights method only works for NPCs, and I'll explain in the next paragraph why. I ended up using a global script which does stop running when the game is saved and reloaded, but which can be restarted by addressing the jailed NPCs.

If a light is added to the player's inventory, it will be invisible if it has no icon art, but a cryptic message will appear: " has been added to your inventory". A light has a togglebox saying whether it can be carried. If this toggle is off, it's impossible to name the light. I checked the togglebox and called the light "A mission" so the message would be "A mission has been added to your inventory". But then I got a default "missing art" icon in my inventory! The light doesn't have a mesh either, so if I dropped it, I could never pick it up again. Incidentally, if its own script makes it drop itself from my inventory, this counts as deletion and crashes the game. So if I put lights in the PC's inventory, either to keep a script running or as an alternative to a global variable, I will get either unwanted messages or unwanted icons, and the risk of littering the game with invisible objects. If used for scripts, the lights also risk littering the PC's inventory, as there is no safe way to delete them.

Adding lights to a non-standard slave's inventory to show his "freed" status, I also found that even when the "carryable" setting is off, the slave will still carry the light when it gets dark, meaning, the slave's arm will take the carrying position even though holding nothing visible, and light will reflect off the slave. So I stored the slave's status in a global variable. In short: lights are fine for transferring the value of local script variables (which can't always be queried directly) in short interactions, but should preferably be cleared from the NPC's inventory when no longer needed.

Lying down

I wanted to make the PC train his/her own companion. This is pretty unbalancing, so I added a malus: it would be very, very tiring. The first time, they would even drop from exhaustion together to drive home the point. How to get them horizontal?

Playgroups have been differently implemented between versions of the game executable, but let's just assume everyone out there has a completely updated game. I used playgroup Death1 and Death5 (lying face up and face down). This only changes the legs; if the arms were in a fighting or shooting position, they stay that way. If the NPC was running, they even continue to move. So the arms must already be made "idle" (not engaged in a different animation) before those playgroups are used. And the playgroup command will not work on the player.

So, let's use Fall! Set Fatigue to 0, tell NPC to Fall. First, all ongoing animations must be stopped by setting the playgroup to Idle, or the NPC will simply keep going and flop over unexpectedly later. Secondly, the NPC will only fall when trying to move and finding there is no energy to even keep upright. The moving happens when the NPC is in following mode. I thought I had to switch off following mode, but the problem turned out to be that Fatigue was rising and with it, the NPC's ability to stand upright. (Of course I used ModCurrentFatigue, not SetFatigue which changes the base value.) So I had to make a script that kept the NPC's Fatigue below zero by constantly lowering it. The NPC does have to stay in following mode to fall down at all. Also, the falling shouldn't happen until the talking dialog is closed, so the script should check if all menus are closed before continuing. Now, I have a NPC lying at my feet. The script that keeps lowering his Fatigue has a timer and will stop running after, say, 60 seconds. (What position you get from Fall seems to be random; when falling to the side, I noted the arms do change, otherwise, they may stay in the last position they were in before falling.)

An observation here is that ModCurrentFatigue used on the NPC does not automatically minimize itself to 0; a command "ModCurrentFatigue -100" on a NPC with 40 Fatigue sets the Fatigue to -60. I don't know if this applies to all characters or only to those with "auto-calculate" unticked. A Fatigue of 0 or less forces the NPC to fall but still allows the NPC to follow when the PC gets too far away.

Since follow mode is what keeps the NPC down, the NPC will rise to follow me and then drop again if I walk away. The NPC will also rotate if I walk around him. So I have to disable PC movement until the timer runs out. But if I talk to the NPC, even though he is following me, even with companion share disabled, any other NPC will hiss at me for supposedly trying to steal! I don't know if this is due to the game itself or one of the many little tweaking mods I downloaded, but the safest method is to disable everything with DisablePlayerControls and only reenable everything after the NPC has had his little nap - leaving me stuck for the full sixty seconds!

So that's how to make an NPC lie down for any length of time. I still don't know how to make the PC lie down. I can use ModCurrentFatigue to lower the PC's Fatigue below 0, but this will only work for a moment. I tried to use SetFatigue 0 on the player and ended up with a player stuck to the ground with a worm's eye view of the surroundings until drinking a potion from its inventory, after which it had full Fatigue again - recalculated from the stats? - but before that, the PC couldn't move, for some reason I couldn't even switch to vanity mode to check its position. If the inventory had been empty, I don't know what I would have done.

Conclusion: I've found out how to floor a NPC, but have yet to come up with a solution for the PC.

Evil twins: duplication of a unique NPC

Beware, beware of accidentally making two or more instances of a unique NPC. By working on a companion mod and loading an old game with an updated version of the mod, for instance, so that the companion who is already tailing you also appears anew in the deserted mansion where you're supposed to meet him. Two companions: twice the fun! Only, this companion had a number of external scripts running on him which now apparently ran on his clone, since he no longer kept himself fed and watered from his own inventory. Invisibly to the user, instances of any NPC are numbered; when a running global script refers to a NPC, apparently it takes the newest instance of that NPC.

So I disabled the clone with the console, opened the mod in TESCS, and added

if ( GetDisabled == 1 )
setdelete 1
return
endif


and then reloaded the game, hoping to be rid of the clone for good. The single companion I now had, had the NPC's starting inventory; the game had considered the older instance "disabled" while loading the newer one that I'd disabled with the console! Either that, or the older instance had somehow been reset, losing all the loot I'd given him to carry. (Or I'd been really stupid and disabled the older instance, when I thought I'd clicked on the new one.)

After that, teleporting the remaining NPC instance had the effect of disabling it. I tossed the game and started a new one.

Since then, I've found another way of creating evil twins: make a mod that influences the Ienith brothers. Oops, Ranes Ienith can't be altered through an external script because he hasn't been placed in the game; he is created the moment the player enters the basement to kill them. So, in the mod, place him in the game. Now you can reference him in scripts. Next, make another mod which also references him in scripts and which also places him (in fact, copy the first mod, strip out what you don't need and add new stuff). If you add the second mod to an old game, the basement has already been loaded, complete with one single new NPC. But if you start a new game where both mods contribute to the building of the basement, they will both put their instance of the NPC in it, and the scripts will only affect the first one, so while the first Ranes gives you a laid-back greeting, the second one will still attack. It is not (officially) possible to make the first mod depend on the second one. It is impossible to tell one mod to not load the NPC if another mod has already loaded it. The only solution: make an ESM which does nothing but place the NPC in the cell. Then have both plugins depend on the ESM.

Journal error in result field

A stupid mistake that had me tearing my hair out trying to find it:

if ( GetJournalIndex <= 40 )

Note, no journal name. This was used in a result field for a response of a certain NPC. When that NPC responded, I had an EXPRESSION error message for that NPC's script, though the NPC's script contained no errors. Apparently the result field is latched onto the NPC's script in-game.

Another stupid mistake in the result field:

if ( GetJournalIndex IN_Journal 40 )

Note, no operator. "Error check results" does not report this. In the game, when the response with this code in the result field comes up, there is a ringing error sound, which shows that something is looping, until finally the game crashes.

Another one, in a script this time:

if ( GetJournalIndex Journal IN_Journal 40 )

Note the extra "Journal", result of a careless copy/paste action. This gave me a "LeftEval" error when the script was run.

A non-journal result field error worth mentioning here: I used a global variable in the result field that had not been defined, ie. did not exist. So in the game, the line where I used this variable and all the code under it, was ignored. Strangely enough, "error check results" gave no warning.

Topics, the blue stuff

Making topics appear blue on time, so that they can be clicked on: I ask Fargoth about "(The imperials have) taken everything", which sets my journal to 10, and he tells me he would die for some nix-hound meat, which reply leaves a line in my journal, setting it to index 20. The topic "nix-hound meat" is programmed to turn blue at journal index 20, but doesn't. This is because the journal index is set to 20 after the dialogue is displayed, and the dialogue is not updated to make an already displayed phrase turn blue. To keep the conversation flowing smoothly, I have to program the topic "nix-hound meat" to appear even before the journal index goes to 20, in order to make it light up in blue when Fargoth mentions it. If another character has mentioned it previously, it will now appear as a topic in the topic column even before Fargoth brings it up, but fortunately he is the only one who does.

The dialogue is updated to change blue lettering back to normal lettering when a topic is no longer valid, for instance, if I bring Ajira the flowers she wants while she tells me to go to lake Amaya, my chances of finding out more about lake Amaya are shot as the letters lose their colour.

See also Straight from the result field for the effect on topic processing of letting a script add the journal entry.

If one plugin (esp file) has a certain topic, say "skills", and another plugin has that same topic "skills" (though obviously with different lines) and that topic does not exist in an esm (master) file, then the topic will only have the lines of the last-loaded plugin. This means that a topic may no longer turn blue when you expect it to, because it has been superseded by a newer version of that topic with new conditions for when the topic is supposed to be blue. The solution is said to be using "AddTopic skills" in the result field to at least make the topic appear in the list of topics, although whether this will produce the desired response is another matter. AddTopic can also be used in Fargoth's example to make "nix-hound meat" appear in the topics list before it turns blue, where hopefully the player notices it being added. Which may not be the case if the list is already quite long and the topic starts with a letter way at the back of the alphabet.

Speaking of modders' topics, an informal standard set by the "Got The Time" plugin is to have completely generic but often used topics start with a double dash, to place them above all other topics. I decided to adopt this standard with the "Coordinates please" plugin.

Related to blue (ie. active) topics: if a topic's condition is, say, a journal entry having appeared or a global variable having been set, then as long as this condition is not met, the topic will simply not be active. However, if a topic's only condition is Disposition, meaning, it should only appear when a NPC likes the PC well enough, the topic will always be active, but until the disposition is at the right number, the NPC will say things like "I don't like you well enough to talk about that". So to make a topic rely on disposition yet keep it absolutely hidden until the disposition is right, extra measures are required.

Deleting and importing topics

On a modding forum, I found the method to remove unwanted parts of a plugin: go to Files to open the dialog that loads a plugin, select that plugin and click on the button Details. A list appears of all additions the plugin contains; selecting an item on the list and pressing Delete puts an "I" (Ignore) before that item, and when the plugin is saved, that item will be flushed out. This method is mainly used to clean up dialogue. However, each dialogue string is linked to a string before and a string after it, and deleting added dialogue can have unexpected results. I had added a number of strings somewhere in the middle of a Greetings section (Greetings 1, if I recall correctly), and set the topmost string to Ignore. When I tested in the game, the expected greetings did not appear, and no wonder: after saving, which flushed out that topmost string connecting the rest of the strings to the standard strings above them, these strings had been relocated to the very bottom of that Greetings section.

Journal entries are usually ordered from 0 to whatever their highest number is. But sometimes, the order is reversed. I can now guess why. When I export an added topic to file, completely delete that topic and all its strings with Ignore, then import that topic again from file - in other words, when the import creates a new topic in the dialogue database, rather than adding the strings to an existing one - the strings are ordered backwards. This is not a problem with journals, but can be with topics because the dialogue conditions are evaluated from the top down, with the most generic response at the bottom.

On the other hand, when responses under an existing topic are re-imported into a plugin, possibly after a spellcheck of the export file, and this is a standard topic that the plugin adds strings to, the standard strings above and below the added strings will be marked with an asterisk and added to the plugin as "changed", so the dialogue has to be cleaned up again by setting any string that isn't added or genuinely changed to Ignore. This happens with every import, so, importing dialogue is a pain.

Local, global, targeted, variables

There are three kinds of scripts: local, global, targeted. A local script belongs to an actor or other object and runs as long as that actor/object is close enough to the PC (main game character); it can't be stopped with "StopScript". A global script is not attached to any actor or object, so there is no default target, as it were, to apply commands to; also, it runs non-stop no matter where the PC is, although it can be stopped with "StopScript". A target script is a kind of ad rem local script: it is not attached to anything, but can be told to attach itself to an actor with "Actor->StartScript TargetedScript" and then acts like a local script, only it also keeps running no matter where the PC is, and can also be stopped with "StopScript". This info is straight out of MSFD9, but here's why I'm repeating it:

I use a script, StayWithCrassius, to have my companion stay with Crassius Curio for a while. The script tells the companion to stop following me and go stand next to Uncle Crassius. It also fills his health and magicka bars to their original value, which I know because that value has been stored in variables in the companion's own script. This script does not have direct access to the companion script variables, so I code it:

SetHealth mycompanion.basehealth

This doesn't work. And if I put this line in a result field, I would get a complaint that SetHealth (and by extension any Set/Mod command) can only take local variables. I can't define variables in a result field, but this is a script, so I add variable "tempval" to StayWithCrassius:

short tempval

set tempval to mycompanion.basehealth
SetHealth tempval

This compiles without problems, but still doesn't work. Oh wait, the script is not targeted (it's called once and terminates itself after running) so maybe I ought to specify whose health should be set?

short tempval

set tempval to mycompanion.basehealth
mycompanion->SetHealth tempval

This will produce an expression error of the LeftEval type when the script is run in the game. The only way to get around that is to make the script targeted. So in a result field I put:

mycompanion->StartScript StayWithCrassius

instead of just

StartScript StayWithCrassius

and now the script will work. The "mycompanion->" bit in "mycompanion->SetHealth tempval" is now optional, but the tempval variable is not; Set/Mod commands don't like directly being assigned other people's variables.

At this point I should mention that there are also three kinds of variables: global, local to the script you're dealing with, and local in other scripts. Actually, there are just two types of variables, global and local, and I only divided the local type into "local" and "remote local" because of problems like the case above, where one script can't access the local variables of another. Global variables are accessible by all scripts, and always keep their value, but can update a bit slowly (eg. I can set the global variable that changes a NPC's responses from normal to happy, but it takes a while for those happy responses to appear). Local variables in local scripts assigned to objects/actors also keep their values, and can be accessed through "ThisActor.localvar", although, as shown above, this doesn't always work. Local variables in global scripts lose their value after a while, and are accessed through "ThisScript.localvar" which likewise doesn't always work. Local variables in targeted scripts are accessed like those in global scripts, since the targeted script is a form of global script, and I don't know if they retain their value; probably not. In any case, all these types and subtypes of variable don't like being mixed. To avoid strange LeftEval and RightEval error messages, it's best to define a "short tempval" in every script that interfaces with other scripts and/or global variables, and transfer any outside variable's value to this tempval for further processing.

I should also repeat something else from MSFD9: if a script contains a local variable defined in another script, but not in itself, the compiler doesn't report this error, and the script malfunctions in the game. Because I use tempval and helpval in various scripts, I don't get a warning if I use tempval in a new script that has no "short tempval" at the top. This had me tearing my hair out and cussing the TESCS until the penny dropped.

Trying to make an expression work too hard also causes eval errors, and I'm not always sure what causes them, too many arguments or the arguments being mixed local/global variables. Here, the cause has to be too many arguments:

set tempval to ( tempval + ThisActor.localvar1 + ThisActor.localvar2 )

The error was an EXPRESSION error, maybe from using the same operator twice?

This causes a LeftEval:

if ( ThisActor.localvar - AGlobalVar <= 0 )

And this causes a RightEval:

if ( ThisActor.localvar <= AGlobalVar )

But if I change the second example to

set tempval to AGlobalVar
if ( ThisActor.localvar <= tempval )


it works. As the examples suggest, this was a targeted script using a global variable, a NPC script's local variable, and its own local variable tempval to make them cooperate. In a local script, the following

if ( localvar - AGlobalVar <= 0 )

can also cause problems, so it's best to combine the two values in another local variable, which I'm again calling tempval, before proceeding:

set tempval to ( localvar - AGlobalVar )
if ( tempval <= 0 )

Sometimes it will seem as if global and local variables don't work together when, in reality, a function simply won't accept a type of variable (SetPos which won't accept globals) or any variable at all. In my experience, AddItem and RemoveItem will not accept variables. This is annoying when trying to empty an inventory of, say, all arrows: it is possible to use GetItemCount to put the number of arrows in a variable, but it is not possible to use RemoveItem with that variable to have that many arrows removed. The only solution is to remove arrows in a loop, one at a time, checking with GetItemCount in each iteration whether all the arrows are gone yet.

What is funny about accessing remote local variables is that, without targeting, you can set their values but not get their values. Sometimes, strangely enough, you can't even set them! But that may be a matter of brackets. These two lines of code in a script targeted on an NPC (where the NPC has its own script and local variables) will work:

set companion.INBintelligence to ( companion->GetIntelligence )
set companion.INBathletics to companion.realathletics


But this line will not:

set companion.INBintelligence to ( LocalVarIntelligence )

because of the brackets around the variable name. And I suspect that

set companion.INBathletics to ( companion.realathletics )

wouldn't work either.

Block if-endif too large

Sometimes I get a RightEval or LeftEval error message in the game from a script that compiled just fine in the editor. And I don't always get the same error.

The problem is with if-endif blocks that are too large. The compiler doesn't check for this, but if I have a huge if-elseif-elseif-etc-endif block that has about twenty elseifs, and the script finds the twentieth elseif to be true, and this is beyond the amount of code that the game can oversee in one block, the game suddenly realizes that this bit of code is "cut off", and the equation is no longer complete. It's like lopping off the end of a long sentence that doesn't fit on the line, and then realizing you can't find the period. If the first or second elseif is true, the rest of the block is not evaluated, and the error doesn't occur.

The solution is smaller blocks, for instance, five if-elseif-elseif-etc conditions, then, if one of these has been satisfied, a StopScript/return, then the next five if-elseif-elseif-etc conditions, and so on.

Dialogue and greyed-out fields

The dialogue editing screen allows me to add responses to a topic for everyone (all speaker fields empty) or for NPCs of a certain race or class or other group (choose race/class/etc. from drop-down boxes). Or I can choose an actual NPC ID, which makes the race/class/etc. choices moot, so, if these were already filled in before I chose the NPC ID, they become greyed out. However, they are still in effect! I added a response for the class Slave and changed the NPC ID from "blank" to a NPC who was not a slave. Consequently, that response didn't appear in the game. I had to go back to that response, set the NPC ID to "blank", then set the now editable class field to "blank", then select the right NPC ID again. In this case my mistake was to leave a value in the Class field, but no doubt the same applies to the other speaker fields.

Special local variables

This is a continuation of Local, global, targeted, variables but was given its own header because that block is already quite large. Most slaves in Morrowind have a local script called "slaveScript" which keeps track of whether they are owned or freed. The slave called Yakov (found at the Suran Slave Market) does not. So I made a script called something like YakovSlaveScript and ran it targeted on Yakov. That's how I discovered three things:

1. There are special local variables that can be defined in an NPC's local script to give that NPC certain abilities: nolore, companion, stayOutside. They can't be defined in a targeted script. Well, they can, but they'll have no effect.

2. And, of course, if you define a local variable like slavestatus in the targeted script and then make a response in the dialogue editor depend on that variable, it won't work either because dialogue responses can only depend on local variables in the local script of the NPC being interacted with. Moreover, the variable can't be used in the result field. I tried something like "MessageBox "Slavestatus %g" YakovSlaveScript.slavestatus", without success.

3. And, even more importantly, local variables from a targeted script can't be queried by another targeted script. Makes sense, as both scripts are targeted on an object (which does not have these local variables) and not on each other. The other targeted script could do something with, for instance, "YakovSlaveScript.nolore", but that way, it depends on a script and not on the NPC. Not that it matters, as a targeted script is still a global script and only one instance of a global script may be run at a time, ie. there would be no other YakovSlaveScript running.

In vainly trying to give Yakov normal slave features (other than by adding the slavescript to the NPC, a change that might be made undone by a newer plugin changing, say, his appearance or abilities) I also discovered, in an effort to add choices to slave dialogue through a script that displayed the same choice options after every response, that local variables from a local script also can't be queried by a targeted script. As an example, say I add companion and stayOutside to "slaveScript", which means every slave can now carry my stuff and be told to wait outside. I then add a dialogue topic "follow" with a response for Khajiit slaves (the "third person" people) and other slaves. So I don't have to put the same list of choices (including: "Follow, but stay outside") in the result fields of both responses, I stick them in a script called "SlaveFollowChoices" (which terminates itself as soon as it has displayed the choices) and put "StartScript SlaveFollowChoices" in both result fields, which means the script is targeted on the speaker. This script is run for every slave with whom the PC talks about "follow" and so it uses the slave's local variable stayOutside (added to the enhanced slavescript) without identification:

if ( stayOutside == 1 )

instead of

if ( menelras.stayOutside == 1 )

since it should work for every slave. It won't. When the script encounters this remote local variable, it stops running. I had to put the choices directly in the result fields instead.

SetChameleon

Apart from the Get/Set/Mod commands for skills and attributes, there are also Get/Set/Mod commands for certain effects, like Attack and Chameleon. If I use "player->SetChameleon 100", either in a script or from the console, the PC should become undetectable. I used it in a script to give the player total undetectability for 60 seconds. It didn't work. I thought Sneak was also needed, added a variable to store the player's Sneak skill, used "player->SetSneak 100" and saw that, again, it didn't work.

So I created an Ability - a spell that, when you add it to the player, instantly affects the player, instead of going into the player's list of spells - which sets Chameleon to 100 and fortifies Sneak by 100. No need to store the old Sneak value now, the game will take care of that. And the script added the spell, counted to 60 and then removed the spell. This time, I saw the PC become see-through as a sign that it worked.

GetDeadCount and the prefix

GetDeadCount works like GetItemCount. The object to be counted comes after it. Yet I had the idea that it worked like GetHealth and had the object before it, with a prefix:

if ( yakov->GetDeadCount > 0 )

This was at the top of a script, and all code under it was ignored. I soon discovered the correct syntax:

if ( GetDeadCount yakov > 0 )

The funny thing is that the syntax with prefix was not reported as an error either when compiling the script or when making the same mistake in the result field and then running Error Check Result.

The Firemoth plugin

Not quite about modding, but falling under "technical information": the official plugin "Siege at Firemoth" causes problems with a lot of fan-made plugins. This doesn't mean that there is anything wrong with the fan-made plugins. I'm still thinking what to do about that. Changing the file date to make that plugin load last hasn't helped.

Collision boxes and the quicksand effect

Collision boxes are the invisible boundaries of a 3D model's "personal space". If a 3D model had the shape of a cube, its collision box could be exactly as big as its outline. But 3D models often have very irregular shapes. Take a bush, for instance. It will be widest in the middle. But if one were to draw a box around the bush that would totally contain it, there would be plenty of free space at the top and bottom that is not occupied by the bush, yet falls within this box. Collision boxes serve, as the name suggests, to detect collision: to see when shape A touches shape B. If a real person walks into a wall, the solidity of that wall is enough to stop that person moving, and the person will be sensitive enough to be (often painfully) aware of having just collided with a wall. But 3D objects rendered in a computer game are neither solid nor sensitive, and their collision boxes are used to fake both.

I found this out when constructing a cosy little dungeon that starts with a slope leading into a main room which then sends out several corridors into other, lower rooms. One of the corridors ran exactly under the slope, but with plenty of room between them. Nevertheless, when a character walked down the slope, I would see that character sink into the floor up to the armpits, to finally drop into the corridor below. How could this happen?

Looking at the collision boxes in the editor made everything clear. The collision boxes of the slope and underlying stairway touch each other. This is because both collision boxes are very high to stretch from the highest to the lowest point of these diagonal shapes. Since they touch, the game is confused about where one ends and the other begins, and the character drops from one into another.

I replaced the slope with sections of straight corridor, which, being almost cube-shaped, had collision boxes barely bigger than themselves. No more touching. Problem solved.

Expansions and missing dialogue

The following will never happen when simply adding dialogue to a Morrowind-only plugin in a Morrowind-only installation. I was making a Morrowind-only plugin in a GOTY installation using Wrye's GMST Vaccine ESP to prevent unwanted GMSTs in the plugin. Now Tribunal and Bloodmoon, as well as adding GMSTs, add a lot of dialogue, especially under generic topics like "someone in particular" and "specific place". My plugin also added dialogue under these two topics. And those under "specific place" never showed up. I didn't understand why, as I'd hung both inserts somewhere under the topmost line in the dialogue present in the original Morrowind gamefile. For "someone in particular" the dialogue was ordered like this: first a block of expansion dialogue, then the old dialogue. That's why my added dialogue for this topic showed up. For "specific place", the original and expansion dialogue were not so neatly separated. That's why, although I thought I'd added lines in the "old dialogue" section, they were in fact under an "expansion dialogue" line. The plugin relied on "Morrowind.esm" only, but the line ID that my added lines started under did not exist in this ESM, hence, the lines were not shown.

Conclusion: when adding dialogue to a Morrowind-only plugin in a GOTY installation, make sure the dialogue is inserted under a Morrowind-only line. I moved the lines so they came under Elone's reply about places the player should know about, and that solved the problem.

Two ways to cause a CTD through dialogue

The first way to crash the game through code in the dialogue result box is one that I could have seen coming. Morrowind Scripting for Dummies warns that two or more text displays in the same frame could cause a crash. I have the PC do something for an NPC. And the NPC says a big "thank you" and gives the PC a bundle of stuff. So far, so good. But because there is so much to receive, all the "XX has been added to your inventory" makes the dialogue window scroll upwards so fast that the big thank-you is completely lost from sight. So I split the response up into two parts and put "Choice "Continue" 1" under the first part. The idea is: text1->Continue->text2 and give stuff. But now I make a mistake: I put the code to give stuff in the result field of text1, not text2. So, while the dialogue window is waiting for me to click on "Continue", the messages about all the stuff I'm getting pop up in separate little windows. Then an error message pops up saying something about "menu pointer error", followed by a CTD. Note to self: always put alter-inventory code after a "Continue".

The second way requires a script to dynamically generate choices. Normally, choice code is put in the result field and there are two or more choices. In this case, I want the player to be able to train a companion in all skills the game knows (that's an awful lot of choices) as long as the player has more points in that skill than the companion (that eliminates a few choices). So for every skill, I have to compare the player and the companion to determine if the choice will show up. For this, I write a script (something like CalculateChoices) which is run from the result field and shows the appropriate choices. When they've just met, the companion's skills will be poorly developed, hence the number of choices shown is so big it makes the dialogue window scroll upwards, and... CTD. The solution is to subdivide the choices: first have the player ask what attribute to work on, then what skill belonging to that attitude to train in. That means a crash-safe maximum of seven choices per question. Both sets of choices are presented by the same script, which also allows the companion to offer training to the player (again, only for skills that the companion is better at). Oooh, I'm so clever.

What NPCs will and won't use

The case for this tip is Mistress Dratha. This venerable old magic user has maybe two spells in the game. That's not much, so in a general startup script I added more spells to her character. Then, I added some dialogue so that she wouldn't just get the ring of Black Jinx delivered to her; she would have to work for it, by battling three atronachs in the Arena. And they killed her every time, because she just wouldn't use the spells that were added.

So in the script that made her fight the atronachs on entering the Arena, I also added scrolls that she might find useful. She only had two scroll types in her original inventory, one for spell absorption and one for a shock attack, to which the storm atronach was, of course, immune. No wonder he kept finishing her off. Checking her corpse, I found she had used the extra spell absorption and shock attack scrolls I gave her, but did she use the sixth barrier scroll? Noooo! I've noticed with companions also that they will not use healing scrolls on themselves in a fight, although they will use healing potions, which is a shame as NPCs tend to drink all healing potions at once but will only use one scroll at a time. At any rate, I conclude that as with spells, NPCs only use scrolls that they were given in the game editor.

So I added a potion to protect against frost and shock attacks (being Dunmer, she was already fire-resistant, and the flame atronach was always the first opponent to go down) and lo and behold, she used it. So game AI will let NPCs use "strange" potions, at least during combat and defensively, and I assume if the potions are standard and not a player-brewed concoction (but this hasn't been tested).

With weapons and magical items, the opposite appears to be true: they will use a shock or frost ring in a fight, but not a healing ring. They do use a barrier ring, but not, it seems, a robe that casts Reflect. Sadly, guaranteeing an effect by letting them wear a "constant effect" item (a belt of Feather so they can carry more?) is a bad idea as "constant effect" starts to work backwards on an NPC after a while. Letting them wear an item that constantly regenerates health only during battles might be a safe way of keeping them alive.

In addition, I gave Dratha three soul gems and let her cast Soul Trap on the atronachs (NPCs can cast any spell when the script tells them to, whether they have that spell in their inventory or not). This worked, that is to say, when she killed the first atronach, I received a message "you have captured a soul". I didn't check whether the soul had gone into a gem of mine or into the soul gems I had added to her inventory, but it was a moot point as, apart from the undesired message, she could only cast Soul Trap once before combat commenced and she skipped the next two times in favour of defending herself. So I just let her give me fully charged soulgems out of nowhere with AddSoulGem if she survived the fight.

Shushing a companion

A NPC in following mode is with you all the way. Kill someone? The NPC will help you without question. Grab stuff that isn't yours? The NPC stays mum and will even carry your loot, companion share permitting. But put the NPC in Wander mode by telling them "stay here", and their conscience returns. Ooh, thief, murderer! Worse, their outraged cries attract others, and if you're unlucky, those others will be guards. Ditto the NPC who is intended to be your companion: do anything wrong before you've asked the NPC to follow you, and you may find yourself running from, fighting or killing your intended companion. The following is info that I got from Morrowind Scripting for Dummies, put to practical use:

I can add the standard local variable "nothief" to the NPC's script and set it. What this does is block the uttered accusations, not the mounting rage itself. This rage is programmed as the NPC's Alarm value: the higher the Alarm, the more likely the NPC is to attack. The commands dealing with the Alarm value are GetAlarm and SetAlarm (default value: 30). The above scripting guide tells me that the variable OnPCHitMe is set to 1 not only if the PC actually hits the NPC, but also if the NPC sees the PC doing something naughty. So, in the NPC's personal script, I add code that sets Alarm to 0 whenever ONPCHitMe is triggered.

For this particular companion the script only sets Alarm to 0 before the companion and the player are properly introduced, as the companion is supposed to criticize the player later. It may be useful in the script of a companion not meant to block the player. Usually, companions will follow the PC so closely that the PC ends up getting jammed in doorways. Some, like Princess Stomper's "Breton Hunk Companion Lite", will always obligingly step out of the way, and others have an option to let me choose between these two modes of behaviour. What happens when the companion gives the player some space is that this companion is set to Wander, which turns on a personal collision detection, until the distance has widened enough and then switched back to Follow. In that interval, the companion may suddenly subject the player to a barrage of accusations and even become aggressive, unless subdued using nothief and OnPCHitMe.

Game lag in Windows 7

I thought this was a problem caused by playing an old 32-bit era game on a computer with a 64-bit processor and a 64-bit version of Windows 7, until I found I had a 64-bit processor and a 32-bit version of Windows 7. I'm still not sure what the exact reason is, but it has to do with "rundll32.exe". Usually, the game starts without problems. Sometimes, I have to click the icon twice before the game runs, and it will be slow and choppy, a Desktop gadget showing the CPU at 100%. Using Ctrl-Alt-Del to find what was running, I saw that the process hogging the CPU was "rundll32.exe". Deleting this process did not stop the game, but sped it up to its normal framerate. So I found a workaround: start Morrowind (click icon twice if necessary), this opens the Morrowind Launcher. Open the Task Manager and delete the "rundll32.exe" process. Then start the game.

I revisited this problem when I wanted to install the Morrowind Script Extender. This is as simple as putting the MSE executable in the same directory as the Morrowind executable and clicking the MSE executable, which will then start Morrowind. If I clicked the MSE executable, nothing happened; if I then clicked the Morrowind executable, it would start, but crash if I wanted to start a new game (as there were no save games to load). So I looked again at what happened if I started the standard Morrowind: first "rundll32.exe" is run but exits, it runs a second time and stays in memory this time, then the Morrowind Launcher opens and the game can be started from there. If I don't use the Morrowind shortcut but double-click on Morrowind.exe, the game starts directly without loading "rundll32.exe", and without lagging. So the problem is the Morrowind Launcher. Fortunately, the textfile included with the Morrowind Script Extender explains how to make a shortcut that will bypass the Launcher and start Morrowind directly.

Script quirks in a Morrowind-only mod

So I had a big messy unfinished mod that needs Tribunal and Bloodmoon, and I wanted to take out some stuff that didn't need Tribunal or Bloodmoon and put that in a Morrowind-only mod. But the big mod used a startup script to dress some nude people the first time it was run, and to check if any pilgrims needed to be removed from the shrine after a finished pilgrimage, every time it was run. And Morrowind uses only one startup script, which I'd rather not alter. A recommended solution for a script that only needs to be run once is to attach it to a ring or other small object, and then hide the ring in Seyda Neen, so it will be loaded, and its script run, when the player first steps out of the Customs and Census office. But my script needed to be run preferably at least once per game. So I attached it to four rings and hid these in Seyda Neen and three other places where the player was bound to go every so often (Vivec, Ghostgate, Balmora near Caius Cosades' house). They worked like this: if a CellChanged occurred (the player either wanders into or out of the cell where the ring is) then the script would run, and relocate NPCs if needed.

And it didn't work. When I started a new game, Morrowind responded to the script as it often does to scripts when Tribunal/Bloodmoon are not installed:

Trying to RunFunction index greater than function count

And the script aborts. This is apparently because the ring's script doesn't wait for the intro and tutorial to finish, but runs straight away; a problem I've never had with the startup scripts that the expansions allow me to add. So in this Morrowind-only workaround-startup script, I have to add code to stop it running until the game has properly started:

if ( CharGenState != -1 )
return
endif

That took care of the error message, although the script still didn't run due to a copy/paste error that produced a LeftEval error. That this error message has something to do with the difference between Morrowind and Morrowind plus expansions is shown by a "Skip Tutorial" mod that I always use which works perfectly under GOTY, but whose altered "CharGenDoorExitCaptain" produces the same RunFunction message under Morrowind.

Having found that "startup" scripts need to be stopped from running too soon, I found another Morrowind-only quirk: CellChanged doesn't always work. In the Arena in Vivec, there is a hidden ring with a script attached, just like my "startup script" rings. And, just like those rings, the script only runs when "CellChanged == 1", in this case, when the PC steps into the Arena. Then, if there is any fight scheduled, the appropriate opponent(s) will be teleported into the ring. But I wanted to bring Dratha over to fight some atronachs. So I made a second ring based on the first, that also ran after a CellChanged event (but only if the first ring hadn't started any duels). In the GOTY plugin, I tested this and it works. In Morrowind, it did not. Apparently, if two scripts in one cell check for CellChanged, only the first one gets accurate feedback. So the second one has to use another method. Fortunately the second ring was only needed for one fight, so I could define a variable "doOnce" and use that instead of CellChanged.

Conclusion: the expansion sets not only add new features, but fix stuff that is broken: stuff that the Morrowind-only game patch doesn't fix.





Previous Top Next