Aug 10, 2010

While we wait: What I have learned of the nwn2 toolset

The more I try to fix all the crap in the toolset or engine issues, the more I realize you will never fix it all, even if you waste that time. The thing about most of the bugs (primarily for my troubles and this example, initiating a conversation in a fail proof way) is that they are kind of low percentage random fails. You can't figure out why they fail so build these elaborate catches and exception scripts and then 3 people test it and it fails in two more ways.

For those who insist on using this broken product, of which there are several reasons to do so now that it has mature custom content, allow my last 3 months of hell to produce a lesson for the rest of you.

The secret cure: knowing that scripts and engine functions will work the majority of the time with random failures, place absolutely everything in question in a repetitious system. The one thing the engine has going for it is that everything has heartbeats. Using this to consistently try and re-try your intended action until it works, is the best fit way to seamless force something to work, even if a small delay gives a player pause, they will happily shrug it off and resume when it works six seconds later.

My Example:
Problem: Companion talks that occur on rest at the tavern

My approach: choose the conversation, assign it to the PC as talking to them self, and let the others join as their nodes come up

Issues:
-PC failing to talk because some action bumped it
-NPCs failing to say their line for whatever reason
-future conversations not making sense because prior ones failed at some point
-Companions /roster members going scripthidden permanently because the conversation failed and the call at the end to restore them can never fire

Fixes:

1 - The PC now, rather than being paralyzed, scripthidden etc etc in a vain attempt to get them to actually follow their action que and converse, simply is given a begin conversation repetitiously with no delay until the PC actually accepts it.

pseudo code:
do {BeginConversationWithSelf(PC) }
while (PCNotInConversation);

I noticed that this can call the script like 2000 times a second, but it works, and as soon as the PC actually accepts and is in the conversation, it stops, satisfied that whatever failed attempts may have occured, it has suceeded now.

I do not advance the conversation "counter" until in the final node of the talk, knowing that the conversation will have executed full at that point and the next rest-conversation is now allowed. This I had in long ago however, due to the random companion failures to speak that can be addressed by this method. I use a set int on the final node of the conversation, so its not directly in the script at all when it comes to advancing the rest talk counter.


2 - Fixing the "stuck invisible" roster members

While resting or leaving the area restored "in-party" members, if a conversation broke and everyone was invisible, the roster members (out of party companions) were gone forever.

I tried and wasted about 2 weeks trying to solve this, setting them to un-invis right before a rest talk (in case they were invised by the previous one) but this was still obviously a bug, I also set it to uninvis all members at the end of the conversation, as well as entering the tavern. While re-entering would re show the roster, in the end it was a bunch of work for what was still, a bug.

Final solution: in the taverns heartbeat script, if the PC is not in a conversation, then neither can his roster members be, there fore, set all roster members to VISIBLE if the PC is in the area and not conversing:

Pseduo-code for the tavern heartbeat script:

if (PCIsInTavern)
{ If (IsPCConversing (FALSE) ) SetAllRosterMembersVisible() };

This second method rather than using an infinite while loop simply fires every 6 seconds on the heart beat. By the time the player even notices they are gone they are already reappearing.

So really there is two options for a fail safe, either an infinite loop for instant trys and re-tries or the heartbeat wait, which is 6 seconds. Note that certain commands could cause an overflow error if using the infinite loop so I recommend the heartbeat whenever possible. The overflow error doesnt appear to be fatal though, it just means the script will fail, but if you are retrying it anyway it should resume the next try once the overflow is resolved.

I got this idea from looking at Obsidian's own way of making a conversation mandatory, via the CreateIPSpeaker() function. It simply spawns an IP speaker and keeps trying it every heart beat.

No weeks of frustration, no blocks of code for every possible failure. Just a clean, single line, that keeps knocking the door till it opens. This is ultimately the best and most efficient way to deal with something you know will randomly fail, and there is nothing you can do about it.

Aug 1, 2010

Current Status

I had to rip through my game again discovering a crap pile of problems in 1 run. The 2nd run the storm kind of faded and it was just 2 things. One of which is a problem with the game itself and nothing I can fix.

So my game is zipped up, complete and playable at any point. Im trying to get people to actually do that for me while I get a couple more vocals into it.

That's where it's at.