Showing posts with label conversations. Show all posts
Showing posts with label conversations. Show all posts

Thursday, March 8, 2007

Riddle Door!

The next stage of the adventure requires the PC's to attempt to pass a riddle door. I'm sure everyone who has any experience in the fantasy world understands the concept - a door is locked, you have to answer X number of riddles to unlock it. In the original campaign, on which this module is based, I allowed the players to figure out the riddles on their own. They varied in success, but in all honesty kicked my (carefully-chosen) riddles' ass. Maybe I had smart friends, maybe I picked riddles that were too easy...either way, I figured to make it a little more difficult in the module.

And how, you might ask? Well, I set the riddles' difficulties according to the players' Wisdom attributes. The first riddle has a threshold of 14, the second 16, and the third 18. If the player's Wisdom meets these thresholds, then they have the answer available to them in the conversation. If not, then they cannot solve the riddles. Okay, so maybe that's a bit harsh (understandably), so I created an item called a "Riddle Book" that will be available to purchase in town that allows PCs to bypass the wisdom checks in order to move forward (I'm nothing if not a forgiving DM...sometimes).

I had a little bit of trouble translating the exact desctiption of the door from the campaign as I designed it into a NWN2 module. The exact description, as I originally wrote it was:

"The keyhole is located in what appears to be the mouth of a demon, and the eyes glow a dim, eerie red. Upon inserting the key, the door animates, the eyes glowing more intently as it swallows the key. It then begins speaking..."

So there's no door that fits that description (and as I said before, I'm no 3D modeler)...so I used the closest thing I could find - the Illefarn door, with some lighting added for effect. And after playing with some of the effects, red just didn't look "right", so I settled on blue for the "glow" effect...



Overall, I'm very happy with the visual effect - it pulses from a white light to a blue light, which is placed behind the door, but leaks out around it. A very cool effect that (unfortunately) you can't really grasp just from the screenshot.

So how does the riddle door work? Well, first of all it's locked, so if the PC's encounter the door without the proper key, they just get a quick cutscene saying "The strange door glows an eerie blue"...

Once they have the correct key, the real fun begins. The door has a custom script on it (assigned to the OnFailToOpen event -- door set to Locked, Plot, no required Key, but Open Lock DC of 99) that I created, called "mod_talking_door" which simply triggers the conversation "mod_goblincave_strangedoor" set in the "conversation" property. This conversation checks to see whether the PC has the key, and has additional conditionals to determine whether the PC has interacted with the door before, whether there are any remaining riddles to be answered, and whether the door should be open:


As you can tell, there are a lot of conditionals and actions involved in this conversation. The general idea is that each of the three riddles have their own "state" in the local int "iRiddles". If the PC hasn't answered any, and hasn't interacted with the door previously, this value is zero. If the PC has interacted but not answered any riddles, the value is 1. If the PC has interacted, and answered 1, 2, or 3 of the riddles correctly, the values are 2, 3, or 4, respectively. If the value is 4, then the door simply opens (assuming it somehow gets closed).

As commented on before, the riddle nodes are in reverse order (remember that conversation nodes trigger on the first node that has a TRUE conditional, and ignores the rest). So we start with the end state, iRiddle = 4, where the door simply opens. We then check whether iRiddle > 0, and if so fire off the second-interaction node, which also then checks whether none, 1, or 2 of the riddles are already answered. Next we check to see if the PC has the "Strange Key", and if so we initiate the riddle dialogue. Finally, we have the generic, no key and no prior interaction "teaser" node.

Also, you'll notice that there's an action called "mod_004_riddle_effects()" with the optional parameter of "1", "2", or "3". This is where the fun begins...each of the riddles has an effect that hints at the answer to that riddle. The first has a blindness effect associated with it, the second has a Stinking Cloud effect (downgraded from the CloudKill effect in the original campaign), and the last riddle has a Summon Skeletons effect associated with it.


// Function wrapper for the Stinking Cloud effect.
// Requires a creature in an inaccessible area to issue AssignCommand to.
// Usage: AssignCommand(oPlaceHolder,CreateCloudAOE(oPC));
void CreateCloudAOE(object oTarget) {
float fDuration = IntToFloat(d6()*6);
effect eAOE = EffectAreaOfEffect(AOE_PER_FOGSTINK);
location lLocation = GetLocation(oTarget);
ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAOE, lLocation, fDuration);
}
void main (int iAction=2) {
// This script defines the actions taken during the "Riddle Door" interaction.
// 1 = Blindness cast on PC Speaker.
// 2 = Cloudkill cast on location of PC Speaker.
// 3 = 1d4 skeletons summoned on waypoint "mod_001_wp_skspawn".

if (iAction <> 3) return;
object oPC = GetPCSpeaker();
if (GetIsPC(oPC) == FALSE) {
return;
}
if (iAction == 1) {
// FloatingTextStringOnCreature("Blindness Effect",oPC,FALSE,10.0);
effect eBlindVFX = EffectVisualEffect(VFX_DUR_SPELL_BLIND_DEAF,FALSE);
effect eBlind = EffectBlindness();
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBlindVFX,oPC);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBlind,oPC,120.0);
}
else if (iAction == 2) {
// FloatingTextStringOnCreature("Stinking Cloud Effect",oPC,FALSE,10.0);
object oPlaceHolder = GetObjectByTag("mod_004_c_StinkCloud");
AssignCommand(oPlaceHolder,CreateCloudAOE(oPC));
}
else if (iAction == 3) {
// FloatingTextStringOnCreature("Skeleton Summoning",oPC,FALSE,10.0);
effect eSkeletonSpawn = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD,FALSE);
location lSpawn = GetLocation(GetObjectByTag("mod_001_wp_skspawn"));
int iSpawn =0;
ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY,eSkeletonSpawn,lSpawn,1.5);
for (iSpawn; iSpawn <= Random(4); iSpawn++) {
CreateObject(OBJECT_TYPE_CREATURE,"mod_c_skeleton",lSpawn,FALSE);
}
}
}


The easiest of these are the effects flagged by 1 and 3. Effect #1 simply applies a blindness effect and animation on the PC attempting to open the door. Effect #3 summons 1-4 skeletons behind the PC(s), at the mod_001_wp_skspawn waypoint. Effect #2, the Stinking Cloud, required a little more work. It appears that doors can't have actions associated with them (as long as they have zero hit points), so I had to create a placeholder creature, in a room waaaay off from the PC's location, to which I could assign the action ApplyEffectAtLocation. I recently read a post that suggested you could get around this by setting the door's Hit Point value > 0, but I haven't actually tested this out.

The fun part about this part is that if the user gets an answer wrong, they suffer some form of punishment - the first answer blinds them if incorrect, the second casts a Stinking Cloud effect on the PC's location, and the third answer summons skeletons to attack if incorrect. The boring part is, if the PC's wisdom is high enough, or they have the Riddle Book, they just pass through and open the door.

Tuesday, March 6, 2007

First Challenge - the Ogre Chef!

Yep, you read that right - Ogre CHEF, not chief. In the original campaign that I had written, the PCs are hired to clear out a cave full of goblins near the town - the usual 1st or 2nd level quest. But in the cave, they encounter an ettin (definitely NOT a 1st or 2nd level encounter). The thing is, though, he's (they are) a chef. A proud chef, who wants nothing more than someone to feed on a daily basis. So there's a conversation to be had, and the PC's go away with their lives intact.

When I was translating this into the NWN2 toolset, though, I found out there's no Ettin creature! And being no 3-D artist myself, there wasn't much likelihood that I would be creating my own custom blueprint to add one in. So I looked through the various critters, and found one that looked promising - the Ogre Magi!!



Sure enough - he looks imposing! So now that my model was chosen, I built the "kitchen" and placed him in it. Next, I wanted him to talk to the PC as soon as they were perceived, so that they couldn't know right away that he was not hostile (though if they sneak or are invisible entering the room, they might figure that out themselves). So I created a UserDefined behavior script that had an OnPerception handler that fires off his assigned Conversation upon his perception of a PC in his range.

if (nEvent == 1002) // OnPerceive event
{
object oPC = GetLastPerceived();
if(GetIsPC(oPC) && GetLocalInt(oPC, "Dlg_Init_" + GetTag(OBJECT_SELF)) == FALSE && !IsInConversation(OBJECT_SELF))
{
ClearAllActions();
AssignCommand(oPC, ClearAllActions());
ActionStartConversation(oPC);
}
}

I also placed a custom OnHearbeat event in the same UserDefined script, that fires off one of several one-liner statements at random:

if (nEvent == 1001) // OnHeartbeat event
{
int iRandom = Random(10)+1;
switch (iRandom)
{
case 1:
SpeakString("Hmmm...need something more...",TALKVOLUME_SHOUT);
break;
case 2:
SpeakString("No, no, no!!! That not right!!!",TALKVOLUME_SHOUT);
break;
case 3:
SpeakString("Ouch! Too HOT!!!",TALKVOLUME_SHOUT);
break;
case 4:
SpeakString("Not enough garlic...",TALKVOLUME_SHOUT);
break;
case 5:
SpeakString("Why meat so stringy?",TALKVOLUME_SHOUT);
break;
case 6:
SpeakString("Stupid goblins, not know what they eating...",TALKVOLUME_SHOUT);
break;
case 7:
SpeakString("Maybe being with tribe not so bad.",TALKVOLUME_SHOUT);
break;
case 8:
SpeakString("This not taste like deer...",TALKVOLUME_SHOUT);
break;
case 9:
SpeakString("Now that is a tasty stew!",TALKVOLUME_SHOUT);
break;
case 10:
break;
}
}

Unfortunately, I later found out that the NPC OnHeartBeat user-defined event doesn't work properly...so I had to copy the default onHeartbeat script into a custom script and paste the pertinent parts (from int iRandom= Random(10)+1 to the end of the switch statement) into the custom onHeartbeat for the Ogre Chef.

So now the Ogre Chef himself is set up, and his conversation has been skeletoned out...then I had to figure out how to structure it and set the appropriate actions and conditionals. This wasn't too hard at first - there's one conversation when the PC approaches, another if they don't agree to help, and a third if they agree to help but haven't completed the quest (the innkeeper in town is looking for a chef...guess who's supposed to get the job!). The only problem that I ran into was that the PC is supposed to have an option to attack the ogre chef...after all, he's EVIL, right? Unfortunately, it's not as easy as you'd think taking a neutral creature and making him hostile, due to a bug in the SetIsTemporaryEnemy() command. *sigh*

But right now that's what he's using, so he attacks the PC if they provoke him, but the default action on him remains "Talk To..." I'm thinking about switching this so that he jumps factions, and then putting a check in the onHeartbeat script to reset him to Neutral after a certain amount of time. But I haven't done that yet.

So the long and the short of it is my first custom character in this mod was created and works (almost) flawlessly. There are a few other things that occur in the conversation:
  • The Ogre Chef checks to see if the PC has a "help wanted ad" item from the innkeeper, if so (and the PC has agreed to help him), the ogre chef gives the PCs a key to a hidden entrance to the Throne Room and disappears (off to town!).

  • If the PC taunts the ogre chef or jokes with him too much, he'll refuse to talk with the PC again.

  • If the PC is extra nice, the ogre chef gives the leader a bowl of his special stew (Cure Serious Wounds)...if taunted, the PCs will never get this handy item (even if they manage to convince the ogre chef to work in town).

  • There are a couple quest journal updates and bluff checks scripted in.
Well, I think that's it for tonight...tomorrow I'll talk a bit about the riddle door that I created, and how I kludged my way through some of the effects (particularly a spell cast in the PC's area upon one particular wrong answer).