Talking Containers

Here’s the scenario: the PC is in a cave where there’s an ambush waiting for him/her. In the cave there are boxes that have goblins in them. The goblins aren’t that smart though, and they’re giving away their ambush by constant chatter. I’ve removed the inventories of the containers and given them both ActionSpeakString and SpeakString scripts to be executed on heartbeat, but nothing happens. What do I need to do to make my containers talk, whisper and snigger like the foolish goblins they contain?

I mean what you describes sounds fine. Random chance of a random phrase when a PC is nearby. Could even have it whispered potentially although not sure placeables can whisper.

You should post your script :grin:

Ok I got it to work, I had a simple switch-case like this

int nLine = d6();
switch (nLine)
{

}

didn’t work. I simply changed the d6 into Random(5) and rearranged the cases to start from case 0 into something like

int nLine = Random(5);
switch (nLine)
{

}

and now it works perfectly. Followup question: isn’t a d6() supposed to return an integer between 1-6? or is the switch block always supposed to begin with “case 0:”? Why didn’t the d6 work?

EDIT: added in the missing semicolons xD

Without seeing your full script I can’t really comment. D6 does a value between 1 and 6 yes.

Nvm that, the old script no longer exists and now it works correctly with both a die roll function and a Random function. It’s been ages since I’ve coded anything and this is the first time I actually try to do something with the Aurora Toolset, been in full autist mode all day :grin:

Also, gotta say that I would’ve been completely lost without the lexicon wiki for nwn, there’s a million and one little quirks that you just need to know.

Anyway, here’s the final version of the script. Looks disgusting and needs a little work on fine tuning timings, such as adding waits between a line and boss response. Also, need to add comments.


#include “nw_i0_plot”

int nWhisper = Random(2);
int nSpeaker = Random(2);
int nLine = Random(4);
object oPC = GetNearestPC();
object oNastyBox = OBJECT_SELF;
object oGobboxA = GetNearestObjectByTag(“Goblinbarrel1”);
object oGobboxB = GetNearestObjectByTag(“Goblinbarrel2”);
object oSpeaker;
string sRebuke = “skriptis haisee”;

void main()
{
if (GetDistanceToObject(oPC) < 10.0f)
{
switch (nSpeaker)
{
case 0:
oSpeaker = oGobboxA;
break;
case 1:
oSpeaker = oGobboxB;
break;
}

 if (nWhisper == 1)
 {
    switch (nLine)
    {
    case 0:
        AssignCommand(oSpeaker, SpeakString("*snigger*"));
        break;
    case 1:
        AssignCommand(oSpeaker, SpeakString("Me smells something funny!"));
        break;
    case 2:
        AssignCommand(oSpeaker, SpeakString("*whispering*"));
        break;
    case 3:
        AssignCommand(oSpeaker, SpeakString("*cough*"));
        break;
    }


    int nRebuked = GetLocalInt(oNastyBox, "REBUKED");
    switch (nRebuked)
    {
    case 0:
        sRebuke = "You shuts up now, or they hears us!";
        SetLocalInt(oNastyBox, "REBUKED", nRebuked + 1);
        break;
    case 1:
        sRebuke = "I said you shuts up now, stupid!";
        SetLocalInt(oNastyBox, "REBUKED", nRebuked + 1);
        break;
    case 2:
        sRebuke = "*Growls*";
        SetLocalInt(oNastyBox, "REBUKED", nRebuked + 1);
        break;
    case 3:
        if (Random(2) == 1)
            sRebuke = "Me really hates you guys...";
            else
            sRebuke = "...";
        break;
    default:
        sRebuke = ("sun skriptis ei toimi");
        break;
    }
    SpeakString(sRebuke);
 }

}
}

Damn that mess really needs some cleaning up to do :pensive:.

Your code formatted -

#include “nw_i0_plot”

int nWhisper = Random(2);
int nSpeaker = Random(2);
int nLine = Random(4);
object oPC = GetNearestPC();
object oNastyBox = OBJECT_SELF;
object oGobboxA = GetNearestObjectByTag(“Goblinbarrel1”);
object oGobboxB = GetNearestObjectByTag(“Goblinbarrel2”);
object oSpeaker;
string sRebuke = “skriptis haisee”;

void main()
{
    if (GetDistanceToObject(oPC) < 10.0f)
    {
        switch (nSpeaker)
        {
            case 0:
                oSpeaker = oGobboxA;
                break;
            case 1:
                oSpeaker = oGobboxB;
                break;
        }

        if (nWhisper == 1)
        {
            switch (nLine)
            {
                case 0:
                    AssignCommand(oSpeaker, SpeakString("*snigger*"));
                    break;
                case 1:
                    AssignCommand(oSpeaker, SpeakString("Me smells something funny!"));
                    break;
                case 2:
                    AssignCommand(oSpeaker, SpeakString("*whispering*"));
                    break;
                case 3:
                    AssignCommand(oSpeaker, SpeakString("*cough*"));
                    break;
            }

            int nRebuked = GetLocalInt(oNastyBox, "REBUKED");

            switch (nRebuked)
            {
                case 0:
                    sRebuke = "You shuts up now, or they hears us!";
                    SetLocalInt(oNastyBox, "REBUKED", nRebuked + 1);
                    break;
                case 1:
                    sRebuke = "I said you shuts up now, stupid!";
                    SetLocalInt(oNastyBox, "REBUKED", nRebuked + 1);
                    break;
                case 2:
                    sRebuke = "*Growls*";
                    SetLocalInt(oNastyBox, "REBUKED", nRebuked + 1);
                    break;
                case 3:
                    if (Random(2) == 1)
                        sRebuke = "Me really hates you guys...";
                    else
                        sRebuke = "...";
                    break;
                default:
                    sRebuke = ("sun skriptis ei toimi");
                    break;
            }
            SpeakString(sRebuke);
        }
    }
}

TR

Damn, is there a way to do that automatically? Looks a million times better already.

If you look in the General Natter area you will see a number of pinned threads including (there are others you should find useful too) -

A Short Tour of These Forums

Be aware that some things have changed since that was written, though the thing you are interested in hasn’t (hint read through to the end before trying to generate code boxes). FWIW, I copied your code into Notepad++, sorted the layout, copied it back and made it into boxed code. So it is not all automatic.

TR

Yeah probably was just an if or switch statement issue. d6 wasn’t the root cause.

For quick questions you can also join us in discord. Things like that are usually quickly solved.

Don’t really use discord that much, but I’m going to keep that in mind. Here’s the final script, cleaned up a little bit and organized it into separate functions in order to make it easier to manage, also comments. I really should’ve written it like this from the beginning, but like I said, it’s been ages since I last wrote any kind of code. This should be in working order now.

EDIT: oh yeah, thanks for everyone and feel free to use/comment the script. Someone might find it here through googling so I’ll just leave it here.

/*This Script is attached to the Boss Goblins hiding place.
The Script is executed on heartbeat*/

//GetNearestPC(); needs this
#include "nw_i0_plot"

/*nWhisper determines the likelihood of a goblin speaking out. A value of 0
results in a goblin breaking their silence while all other values do nothing.
nBossDelay determines the number of slip-ups the boss is going to tolerate
from his goblins before rebuking them again.*/
int nWhisper = Random(2);
int nBossDelay = 2;

/*The fWait float determines the delay in seconds between a goblin speaking
and the boss rebuking. The fMaxDistance is the maximum distance between
the player and the Boss Goblins hiding place for anything to occur at all*/
float fWait = 2.0f;
float fMaxDistance = 10.0f;

//Declaring the necessary objects
object oPC = GetNearestPC();
object oNastyBox = OBJECT_SELF;
object oGobboxA = GetNearestObjectByTag("Goblinbarrel1");
object oGobboxB = GetNearestObjectByTag("Goblinbarrel2");
object oSpeaker;

//The necessary local variables, stored in the boss' hiding crate.
int nBossRebuke = GetLocalInt(oNastyBox, "REBUKE");
int nBossRounds = GetLocalInt(oNastyBox, "ROUNDS");

//Selecting a proper line for the hidden goblin
string GetLine()
{
    string sReturn = "ERROR_GET_LINE"; //if you get this, something went wrong
    int Line = Random(4);
    switch (Line)
    {
        case 0:
            sReturn = "*snigger*";
            break;
        case 1:
            sReturn = "Me smells something funny!";
            break;
        case 2:
            sReturn = "*whispering*";
            break;
        case 3:
            sReturn = "*cough*";
            break;
    }
    return sReturn;
}

//Selecting the appropriate rebuke line for the goblin Boss
string GetRebukeLine()
{
    string sReturn = "ERROR_GET_REBUKE"; //if you get this, something went wrong
    int RebukeLine = GetLocalInt(oNastyBox, "REBUKE");
    if (RebukeLine > 3)
        RebukeLine = 3; //there's only 3 cases, and case 3 should repeat.
    switch (RebukeLine)
    {
        case 0:
            sReturn = "You shuts up now, or they hears us!";
            break;
        case 1:
            sReturn = "I said you shuts up now, stupid!";
            break;
        case 2:
            sReturn = "*Growls*";
            break;
        case 3:
            if (Random(3) == 0) //This rebuke line is supposed to be semi-rare.
                sReturn = "Me really hates you guys...";
            else
                sReturn = "...";
            break;
    }
    return sReturn;
}

//Picking which goblin should speak
object GetSpeaker()
{
    int Speaker = Random(2);
    if (Speaker == 1)
        return oGobboxB;
    else
        return oGobboxA;
}
//All The conditions for running the script.
int Conditional()
{
    if (nWhisper == 0 && GetDistanceToObject(oPC) < fMaxDistance)
        return TRUE;
    else
    return FALSE;
}

//This is SPEAKING!
void Speak(object Speaker, string Line)
{
    AssignCommand(Speaker, ActionSpeakString(Line));
}

//Handling the boss-goblin, rebuking his misbehaving underlings.
//Be sure to remember to use "ActionSpeakString" instead of "SpeakString"
//Otherwise the rebuke is instant and doesn't come with the appropriate delay
void Rebuke(string RebukeLine)
{
    if (nBossRounds < 1)
    {
        ActionWait(fWait);
        ActionSpeakString (RebukeLine);
        SetLocalInt(oNastyBox, "REBUKE", nBossRebuke +1);
        SetLocalInt(oNastyBox, "ROUNDS", nBossDelay);
    }
    else
    {
        SetLocalInt(oNastyBox, "ROUNDS", nBossRounds -1);
    }
}

//ah, a beautifun and lean void main() :')
void main()
{
    if (Conditional())
    {
        Speak(GetSpeaker(), GetLine());
        Rebuke(GetRebukeLine());
    }
}

I’d probably recommend against such use of globals (things defined out of a function or void main() ) since you can trip up a lot but otherwise looks good.

Yeah, I wanted to have easy access to those global variables, that’s why I put them like that. They’re the first thing I see and I can control most of the functionality just by tweaking them. that oSpeaker might be leftovers, gotta look into it.

Suggestion: Change

int nBossDelay = 2;
float fWait = 2.0f;
float fMaxDistance = 10.0f;

to

const int nBossDelay = 2;
const float fWait = 2.0f;
const float fMaxDistance = 10.0f;

Nowhere in your code are these changed so constant declarations are better and are (usually) supposed to be out side of functions.

Also, remove the line -

object oPC = GetNearestPC();

from its current position. oPC is only used once in your code so it is better inside a function. So change -

//All The conditions for running the script.
int Conditional()
{
    if (nWhisper == 0 && GetDistanceToObject(oPC) < fMaxDistance)
        return TRUE;
    else
    return FALSE;
}

to

//All The conditions for running the script.
int Conditional()
{
    int iReturnMe = FALSE;
    object oPC = GetNearestPC(); // If no PC is found returns OBJECT_INVALID

    if(GetIsPC(oPC)) //which makes this check necessary
        if (nWhisper == 0 && GetDistanceToObject(oPC) < fMaxDistance)
            iReturnMe = TRUE;
    
    return iReturnMe; // only a single exit point from the function
}

Hope this helps.

TR

1 Like