Getting a node unique ID inside every conversation scripts


#1

Is it possible to have a unique script that handles most of ACTION_TAKEN scripts inside a conversation based on the current conversation node ?

Example :

I’m currently doing this :

// at_node_1

#include "my_lib"

void main {
    DoSomething(1, 2, 3);
} 

// at_node_2

#include "my_lib"

void main {
    DoSomething(4, 5, 6);
} 

// at_node_3

#include "my_lib"

void main {
    DoSomething(7, 8, 9);
} 

Which is perfectly fine… until you have hundreds of 1 liner scripts (I currently have 171 scripts for a single conversation…).

Considering all the scripts are in the same folder (if you have a solution to organise your scripts in subfolders i would be glad to hear it too ! :slight_smile:) , I would rather have 1 script with a 100 line than 100 scripts with 1 line.

So i want to achieve this single script in all the relevant node of my conversation :

// at_master_script

#include "my_lib"

void main {
    int nTheNodeUniqueID = GetTheNodeUniqueID(); // <-- This is what i'm looking for

    switch (nTheNodeUniqueID) {
        case NODE_ID_1: return DoSomething(1, 2, 3);
        case NODE_ID_2: return DoSomething(4, 5, 6);
        case NODE_ID_3: return DoSomething(7, 8, 9);
    }
}

I’m using NwN:EE if that matters in any way.


#2

There are various ways of working around this.

Some builders use the packages zdialog or zzdialog to parameterize conversations, but I don’t know whether they help to identify the action taken.

My simplest solution is to have generic action taken scripts plot1, plot2… which set a local int on the PC before calling another script with the switch logic. The latter can contain different sections based on the NPC tag, so if n is the largest number of action taken branches in any conversation, the module only requires (n + 1) scripts.

Another solution is to take advantage of the fact that the journal updates before the Action Taken script runs. If you have a dummy journal with a null name and many entries, setting a different journal entry on each Action Taken branch allows you to use the same script on each branch. The script needs read the journal entry, delete it, then switch on that value. One journal suffices for the entire module. The only downside is a spurious log message saying that the journal has updated, when it hasn’t - but players quickly learn to ignore that.

See my Dark Energy module for examples.


#3

You can also make a dozen of generic action scripts, i.e. action_script_1action_script_20 and have each of them check for a corresponding string variable set on the creature which somehow describes what to do / what to check (you’ll need to write a small parser). Or those scripts could even reference a part of the same variable (i.e.|-delimited). They can also alter the variable(s) for a dynamic system, probably most useful for modules with larger number of smaller conversations.

To avoid having 171 scripts, you can break the conversation into sub-conversation (assuming players won’t be jumping between their internal nodes).

It was Obsidian who added script parameters to dialogue nodes. First thing you notice after switching from modding KOTOR to TSL is bugs the much smaller number of scripts in modules.


#4

Thanks for the fast answers !

Some builders use the packages zdialog or zzdialog to parameterize conversations, but I don’t know whether they help to identify the action taken.

These packages seems interesting, i’ll take a look into it !

My simplest solution is to have generic action taken scripts plot1, plot2… which set a local int on the PC before calling another script with the switch logic. The latter can contain different sections based on the NPC tag, so if n is the largest number of action taken branches in any conversation, the module only requires (n + 1) scripts.

This is what I am doing at the moment, but since I have a lot of branches, I still have a lot of scripts. I was hoping there was a solution to bring this n + 1 count to just 1 for the whole conversation. If there is not, I’ll have to reduce my number of branches dramatically.

Another solution is to take advantage of the fact that the journal updates before the Action Taken script runs. If you have a dummy journal with a null name and many entries, setting a different journal entry on each Action Taken branch allows you to use the same script on each branch. The script needs read the journal entry, delete it, then switch on that value. One journal suffices for the entire module. The only downside is a spurious log message saying that the journal has updated, when it hasn’t - but players quickly learn to ignore that.

I don’t think i understand and it feels like a hack, so i don’t think i’m desperate enough to follow that path yet :smiley:

You can also make a dozen of generic action scripts, i.e. action_script_1action_script_20 and have each of them check for a corresponding string variable set on the creature which somehow describes what to do / what to check (you’ll need to write a small parser). Or those scripts could even reference a part of the same variable (i.e. | -delimited). They can also alter the variable(s) for a dynamic system, probably most useful for modules with larger number of smaller conversations.

I have only 1 PC interacting with 1 placeable object through a conversation. The placeable object allow the PC to add / remove item properties to a chosen item.

The branches look like this :

damage
    cold
        1
        2
        ...
        20
    fire
       1
       2
       ...
       20
    ...
alteration
    1
    2
    ...
    20
...

But since there are a lot of types / subtypes with each a lot a values, this can lead to a gigantic amount of branches.
One solution to reduce the number of branches is by reducing the choices to adding +1 and removing -1, but that prevent the user from passing from a +4 to a +20 directly (and vice versas).

To avoid having 171 scripts, you can break the conversation into sub-conversation (assuming players won’t be jumping between their internal nodes).

I don’t think i can do that, since the PC can go back and forth between all the nodes.


#5

In that case, why not try an indirect approach? Instead of “give/take property X” use “Increase/decrease/remove property”. Make the script read current IP data from 2DAs and modify them up and down. You just need three, four scripts per property. For example:

PLAYER: Modify item’s cold damage bonus.

PLACEABLE: Item’s current cold damage bonus is <CUSTOM1000>.

PLAYER: Increase bonus.
PLAYER: Decrease bonus.
PLAYER: Remove bonus.

EDIT: You may also do something completely different and much simpler. Make a chest full of tokens: one for each property and value, i.e. Cold Damage Token +4. Player collects them and places them together with item of choice in a forge of some sort. Then single forge script turns the tokens into IPs (i.e. selects only best tokens in case of duplicates). The reverse removes IPs and recreates tokens in the chest. You can create the tokens automatically by traversing the corresponding 2DA files.


#6

In that case, why not try an indirect approach? Instead of “give/take property X” use “Increase/decrease/remove property”. Make the script read current IP data from 2DAs and modify them up and down. You just need three, four scripts per property

This is the compromise I came up with, which reduces the number of branches by quite a lot. But they are still numerous since there is a lot of types / subtypes

EDIT: You may also do something completely different and much simpler. Make a chest full of tokens: one for each property and value, i.e. Cold Damage Token +4 . Player collects them and places them together with item of choice in a forge of some sort. Then single forge script turns the tokens into IPs (i.e. selects only best tokens in case of duplicates). The reverse removes IPs and recreates tokens in the chest. You can create the tokens automatically by traversing the corresponding 2DA files.

This is a very good idea ! And it is indeed much simpler than the conversation !
The only downside I see is PCs wandering around the module with tokens in their inventory, but that shouldn’t be a real issue, i could destroy the tokens when the PCs leaves the area or something.


#7

Yeah, (z)z-dialog is perfect for this use case but there is a learning curve. It does not help you identify the action taken script per se because you don’t think of it that way. You write a single script with all the “pages” of the conversation defined by building strings adding responses etc, and then a handler for each page which deals with the selection made from the choices on that page. But because it’s all just scipting you can use variables as needed, string building, coloring etc. There’s only one actual NWN dlg file that is used for all of z-dialog based conversations. It has its own set of TAW and AT scripts but you don’t mess with those.

For example it can turn the 600 scripts of a tailoring model conversation into a couple. I couldn’t build without it :slight_smile:

Your (@proleric) generic convo scripting system is pretty good too. I use that sometimes for less generic more personal NPC conversations, plot things. But for all those utility conversations, inns, tailors, temples, banks, etc. z-dialog is great. And well worth the time to learn, I think.


#8

Thanks for your answer ! z-dialog seems very powerful indeed. I’ll take a deeper look when I have the time ! :slight_smile:


#9

Hey guys, a link to z-dialogue would be great about now…

TR


#10

ZZ-Dialog (enhancement of Z-Dialog): https://neverwintervault.org/project/nwn1/script/zz-dialog-166
Original Z-Dialog by pSpeed: https://neverwintervault.org/project/nwn1/other/tool/pspeeds-utilities


#11

Thanks for the links. I figured TR would post them but he prefers to link to his stuff :wink:

Fwiw, I prefer the original z-dialog, although I did crib the token system from zz-dialog. The two are unfortunately not compatible at all.

There is also an enhancement to z-dialog which allows you to collapse all the TAW scripts (zdlg_check_01 - 13) down to 1. There are still 13 unique AT scripts due to the way conversations work. 19 scripts, including the required pg_lists include for the system, plus 2 .dlg files (one zoom and one no-zoom). I’ll try to package up a version with this and a few of the bug fixes I’ve made. Been meaning to do that for a while…

Edit: here


#12

Cheeky… I asked for links because I knew nothing about z-dialog at all before this thread.

TR


#13

I was just teasing you. But it does come right up with the search on the main vault page :wink: Originally both pspeed’s page and zzdialog and now my recently posted update.

It’s been around for years. There used to be threads and discussions about before the great forum migration(s).


#14

I’d agree that Z-Dialog or ZZ-dialog is where it’s at. I found that it could be kind of hard to follow the flow of the conversation once it got to be a certain size, though. I’ve been working on a replacement system for personal use. While it’s not production ready by any stretch, it could be of interest to others.

The downside to systems like these is they require the user to know how to script. I think an automated tool that could write the script for you (like this, for example), would be neat.


#15

That can be true of normal dlgs too. I usually keep a large comment at the top of the complicated ones that shows the flow in a tree format. In fact, I often start by writing that. Then try to use the same idioms in all of the zdlg scripts. But yes, learning curve :slight_smile:

You do need to know how to script, true.

I think that’s worth learning and will be more benefit to builders than learning the language that pwro’s dlg compiler uses.

Also, that one doesn’t solve the problem as it creates a normal DLG which still references all the specific TAW and AT scripts you would need if you used the conversation editor. I looked at that a while back. It could be useful for making regular conversations. The inability to de-compile was a show stopper for me. I’d want to be able to go both directions with it since the raw text is not in the module file (unlike scripts, usually). Otherwise a lot of that tooling replicates the yaml/gff tools but in a non-standard format.