Vanilla engine: When starting the game, don't click "Cancel"/"Yes to all" to skip warnings and errors - if you do, error messages will never display later on in the game.
OpenMW: If on Linux or mac, run openmw.exe from terminal and keep an eye on the output while running the game. If on Windows, test only with vanilla engine.
Playtest with a new character who doesn't know any topics.
Playtest with an old character who knows many topics, has a high level, reputation, etc.
Create new saves for each quest state.
- Is it clear that you're taking on a quest? If you didn't want to do that, is it stuck in your Journal list?
- If not, does it have an option to turn it down? Does it need one?
- Can you come back and take the quest after turning it down?
- Can you abandon the quest? Do you need to?
(doesn't apply to faction quests started from "duties"/"chores"/...)
Whenever the situation changes (usually comes with a journal update):
- Check the new journal entry
- Talk to every NPC involved on every topic associated with the quest. Does their dialogue make sense?
- If the change came from a NPC greeting, reload and contract a disease (console: Player->AddSpell, "rotbone"). Does it still work?
- What if you don't have enough? Does it still work if you have have more than the exact amount of items?
- Are they taken from your inventory?
Taking an item triggers an event:
- Does the same thing happen if you pick it up while the inventory is open?
- NPC walks somewhere. Does this break if you get near them, stand in their way, rest, change cells?
- NPCs attack you or fight each other. What happens if you use Calm/Command spells? If you talk to them, what happens? Does it need to be otherwise?
- If your actions make it impossible to complete the quest, does it properly fail?
- If completed, is the Journal entry closed?
OpenMW currently (2017) doesn't print betacomments. If you use OpenMW, you need to manually select and copy-paste the betacomment output from the console.
One way to 'write down' stuff from inside the game is to use BetaComments. To make that possible, first you have to open your "Morrowind.ini" text file, look for the line:
|Beta Comment File=|
and add a filename eg. "BetaComment.txt" so that it becomes:
|Beta Comment File=BetaComment.txt|
Then when in game, you can open the console (~ / ² , console key might depend on your keyboard), click on the object you're talking about if there is one or on the nearest object (the console needs to have a target), then type
|bc "your comment"|
and it will create a line identifying the object + your comment, in the file BetaComment.txt created in your game folder. Then you can copy+paste these lines or use them to remember the names.
DEBUGGING AND REVIEW
When starting the game, don't click "Cancel"/"Yes to all" to skip warnings and errors - if you do, error messages will never display later on in the game.
Always begin by testing ingame and "on foot" first, without the console. You need to check that any directions can be followed, to see what happens when you enter cells, what else is on the way, etc.
Afterwards you can use the console command coc "Cell Name" (centeroncell) to teleport. If you set up a game saved in a convenient nearby location, do so before adding your plugin to the game (when testing changes to scripts, you'll sometimes need to use a clean save in which these scripts weren't already running). If you need to test many quest paths in many locations, you can set up a save with various teleportation targets using this mod (MWSE/MGE XE only) or this mod (current TR_Mainland.esm cells only), or make your own testing script.
Other testing tools:
- This plugin skips the introduction on "New Game". Edit it to start where you want, add items...
- alternatively, edit "Starting Cell=" in Morrowind.ini
- Another quickstart test plugin
- A more extensive chargen mod
- Some saved games (remove master files) on Modding History
Other useful console commands:
show VariableName (gives value of global variable, or local variable of object selected by console)
showvars while an object is selected shows its local variables and values
tcb (togglecollisionbox display)
tb (toggle cell borders)
tpg (toggle pathgrids)
ori (OutputRefInfo, displays plugin the object comes from, exterior cell coordinates...)
fillmap fills all named exterior cells on the minimap, takes time
ra resetactors, puts back NPCs where they are placed in the CS
coe -3, -2 (CenterOnExterior, CellX, CellY)
ModDisposition 50 (NPC disposition)
Player->AddSpell, "rotbone" (add disease)
Player->SetStrength 80 (attribute)
Player->SetSpeechcraft 80 (skill level)
player->additem gold_001 1000
PlaceAtMe "MyObject" 1,1,1 (place object/NPC/creature)
resurrect creates a NEW copy of the dead npc which will not be properly targeted by compiled scripts
Goodbye (allows you to close if stuck in dialogue loop)
set Gamehour to ... set the hour (0 to 23.99...)
set DaysPassed to ... (to make sure you're using the global day and not a local variable, do not select an object)
set Gamehour to Scriptrunning, "scriptname" and show Gamehour is a hacky way to know if a global script is running, 1, or not, 0 (you can set something else than game hour)
set Timescale to ... (default 30) increases the speed at which time passes in the game. useful if you're testing AI packages since their durations are based on ingame hours
Limits of testing with the console: not everything you can do with the console will work in your plugin's scripts.
- console commands are compiled on the run and generally behave like dialogue results, as opposed to compiled scripts. The console is able to target NPCs that exist but didn't have any instances placed in the CS before; it is also able to target a new instance of a NPC after the original instance is cleansed from the save, whereas compiled scripts would only be able to target the original instance.
- position changes made with SetPos in the console will persist after saving and reloading; when SetPos is used in a script the change will only persist after saving and reloading if the instance was at some point marked as edited in the saved game (changing the value of a local variable on the object's local scripts or using the commands "Enable" or "Disable" will mark it as edited, but simply using "SetPos" will not)
- position changes made with SetAngle in the console will persist even if a different save is loaded without exiting the program
Debugging a script: if you can't immediately identify a bug, add message displays at each stage of the script (MessageBox, "Hello world") until you can pinpoint which part doesn't work. Check the syntax and quirks of any function involved in MSFD; read also its Troubleshooting section for possible meanings of error messages. Advanced scripters: if all else fails and nothing seems wrong with the code, cut the whole script, paste it into a simple text editor (notepad), delete the troublesome line(s) and re-type them by hand, then copy-paste back and recompile.
Go through every single thing added by the plugin in the Construction Set.
You can open it in TESAME on the side to have a list of what it adds, or dump its contents with tes3cmd, or any other method you like, but if you haven't checked every dialogue line, dialogue result, and script, you haven't reviewed the quest.
Quick navigation: in the topics list, press the first letter(s) of the topic name to jump ahead. When in a Greeting or topic, press the * key to jump straight to the first line of the active plugin. Follow * with the spacebar and the first letters of a specific line to jump to that line. Due to the time the CS takes to display everything when you land on a dialogue entry with many conditions, you may have to wait and/or start pressing keys again from the beginning.
Start by opening its Journal entry. You can open several dialogue windows (Character->Dialogue) and keep one open with the Journal stages on display.
You can use the CS Error Check Results to scan for issues in dialogue results, but never save the plugin after doing so (it processes changes to global variables and records them as default values). Error Check Results will wrongly report errors for any local variables inside operations ( + - * ...) or IF blocks if they're implicit (without an explicit speaker target, as in "speaker.localvariable").
Don't rely on the CS script compiler to know that a script works. It will not always warn you about syntax, wrong IDs, or other errors. Tools like MWEdit or others can check your scripts for some syntax errors.
The CS search function (Edit->Find Text) is very helpful, but if you have extremely long scripts, don't rely on it to find portions of text or code in them. It won't.
Cleaning: See warnings about deleting dialogue in TESAME.
Automatic cleaning by tes3cmd will remove vanilla junk cells (those automatically dirtied by the CS) and unmodified objects that exist in the plugin's master files only. To check whether a big file is modifying items in a master (the original Morrowind files) or editing objects defined in another ESP (TR_Mainland or section files), use the tes3cmd function "common".
To avoid merge issues, clean not only dirtied lines from vanilla but also those from other TR files the quest file depends on. Don't keep dialog placeholders dirty (BEGIN TR...), clean them.
Check naming conventions (claims used to generate quest numbers, now add what instead to denote which quest the object belongs to?):
For an object, script, NPC etc:
For a journal:
Initials are faction initials(TR_m3_FG_RatProblem) or town initials (TR_m3_Me_Tunnels).
Further examples: FG=Fighters Guild, HI=House Indoril, OE=Old Ebonheart, Me=Meralag
tes3cmd commands (as a .bat file) detecting common issues with IDs in "TR_plugin.ESP":
- dashes are en-dashes represented by two hyphens -- and have spaces around them
- ellipses are not spaced, ... instead of . . .
- apostrophes and quotes are straight, " instead of “ , ' instead of ` (tes3cmd: to detect the latter use command line and not a .bat file, or see nonstandard characters in commands above)
- when there is both description and dialogue in player choices: [Description in brackets]
- Add Addtopic in the results of dialogue that should link topics the first time, as backup. Use unique topics when possible. If reusing vanilla or older quest topics, check that knowledge of the topic doesn't shortcut other quests.
- Make sure journal entries link the topics the player will need to use or look up for details (whichever topics originally gave the extended dialogue version of what the journal entry says)
- Add ClearInfoActor on dialogue that shouldn't get recorded in the journal's topic index, like any generic dialogue that doesn't give the player any information in a quest topic (not generic topics rumors).
- Add nolore to any generic dialogue. Make sure all dialogue entries have specific conditions (ID, cell or local variable). If the only condition is "choice", try to add more proper conditions anyway, otherwise the CS will display that topic and choices when filtering for any single NPC. If an entry with only cell conditions needs to be common to nolore and not-nolore NPCs, consider adding a TR_Map condition.
- Follow scripting good practices in dialogue results too, keeping consistent spacing around parentheses. Don't keep any spaces around " -> " (error if target was in quotes).
- In dialogue results, if implicit local variables are in an operation ( + ...) or in a IF block, add an explicit target whenever possible (with syntax set "NPCID".localvariable to ... or if ( "NPCID".localvariable == ... ) ) to reduce the amount of wrong reports from CS error check
- If rumors give journal entries, make those "Finish" and let the next journal entries from the questgiver "Restart" the quests.
- Whenever NPC AI is given a task, SetHello 0 until it's completed so it doesn't get interrupted by hello voices.
- If teleportation is disabled by something, always start a global script to enable it back in case a scripted item (Barilzar's Mazed Band...) was used to exit the cell. Update the global T_Glb_GetTeleportingDisabled for easy compatibility.
- Script efficiency: put anything you can under single-frame checks (CellChanged, OnDeath...). Put any slow functions (GetDeadCount, GetDetected, GetLOS) under a timer. Having many lines under a check is not as good as using "Return" at the top of the script under the opposite check. For scripts that run in already slow exteriors, the first few lines should include "return" if quest conditions haven't started, and the next few lines "return" after quest conditions are finished.
- A script that breaks under any legitimate use of ingame mechanics is not acceptable. Assume that stable MCP bugfixes apply, but not features (anything that isn't a default bugfix). MWSE scripting isn't used but you may want to take distant land into account. For extra points test in OpenMW and make sure any advanced scripting works in it too.
Always playtest the file ingame again after any changes made in review!