Aller au contenu

Photo

Arrays


  • Veuillez vous connecter pour répondre
52 réponses à ce sujet

#1
TrekSL

TrekSL
  • Members
  • 32 messages
So I've spoken with a helpful guy here called Shadoow to help with a listening script for something I'm designing. Now, I come fresh off of a java course which is similar to C but with differences. If you look at this test code, it works but for just one word. I could, of course, just make lots of string variables, which might be simple for multiple words and that would be ok, except I was thinking about using an array as a way of keeping it tidy - these will be all strings that i'm storing. However, I've only just learned that NWN doesn't allow arrays but someone mentioned pseudo arrays? Can someone enlighten me?

object oDoor = GetObjectByTag("door");//This is the door to open. Need a global variable
void StartListening(object oPlayer, string sScript)//Two functions here, start listening setting a string on the player
{
SetLocalString(oPlayer,"START_LISTENING",sScript);
}
void StopListening(object oPlayer)
{
DeleteLocalString(oPlayer,"START_LISTENING");
}
void main()
{
int nEvent = GetLocalInt(GetModule(),"GetRunningEvent()");
if(nEvent == 15)//check whether the onchat event Module integer exists when the player enters the trigger
{
object oPC = OBJECT_SELF;//set PC up as oPC
string sMessage = GetPCChatMessage();//sMessage will be whatever is written in chat
SetPCChatMessage();//<--Can be commented out- avoids what the player has typed from appearing onscreen
StopListening(oPC);//disable further listening
if(sMessage == "habe")//<-- this is a simple german learning game, hence the word, nested if statement.
{
SetLocked(oDoor, FALSE);//Unlock my door
}
}
else//script fired from trigger
{
object oPC = GetEnteringObject();
if(!GetIsPC(oPC))return;
StartListening(oPC,"secrettest");//start listen the player
FloatingTextStringOnCreature("You may now speak",oPC);
}
}

#2
CID-78

CID-78
  • Members
  • 1 124 messages
Pseudo arrays is either a set of Local variables or a Localstring, with fixed size or containing seperating symbols.

ie name|str|int|cha could be treated as a pseudo array with four strings in it.

#3
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
If you look at z-dialogue by pspeed, on the Vault, its includes contain pseudoarray functions. Or, for simpler applications like yours, you can just use functions like this:

struct IntList {
    int size;

    int i0,  i1,  i2,  i3,  i4;
    int i5,  i6,  i7,  i8,  i9;
};

struct StringList {
    int size;

    string s0,  s1,  s2,  s3;
    string s4,  s5,  s6,  s7;
    string s8,  s9,  s10, s11;
    string s12, s13, s14, s15;
};

struct SubString {
    string first, rest;
};

/* Return the substring at the given index nIndex of sString, separating
 * sString with sSep. */
string GetStringSubString (string sString, int nIndex, string sSep=" ");

/* Return the number of substrings sString would have if it were separated
 * by sSep. */
int GetStringSubStrings (string sString, string sSep=" ");

/* Return a random string of sString separated by sSep. */
string GetRandomSubString (string sString, string sSep=" ");

/* Return a SubString struct containing the first substring of sString
 * separated by sSep, as well as the remainder of sString. The idiom
 * would be:
 *
 *   struct SubString ss;
 *
 *   ss.rest = sString;
 *
 *   while (ss.rest != "") {
 *       ss = GetFirstSubString(ss.rest);
 *
 *       (do things here)
 *   }
 */
struct SubString GetFirstSubString (string sString, string sSep = " ");

/* Return a StringList struct containing strings extracted from sString
 * separated by sSep. */
struct StringList GetStringList (string sString, string sSep=" ", int nLimit=16);

/* Return an IntList struct containing integers extracted from sString
 * separated by sSep. */
struct IntList GetIntList (string sString, string sSep=" ", int nLimit=10);


string GetStringSubString (string sString, int nIndex, string sSep=" ") {
    int nSep, nStart = 0, nSkip = GetStringLength(sSep);

    while (nIndex-- > 0) {
        nSep = FindSubString(sString, sSep, nStart);

        if (nSep < 0)
            return "";

        nStart = nSep + nSkip;
    }

    if ((nSep = FindSubString(sString, sSep, nStart)) < 0)
        nSep = GetStringLength(sString);

    return GetSubString(sString, nStart, (nSep - nStart));
}

int GetStringSubStrings (string sString, string sSep=" ") {
    int nCount = 1, nStart = 0;
    int nSep = FindSubString(sString, sSep);
    int nSkip = GetStringLength(sSep);

    if (nSkip < 1)
        return 1;

    while (nSep >= 0) {
        nCount++;
        nSep = FindSubString(sString, sSep, nSep + nSkip);
    }

    return nCount;
}

string GetRandomSubString (string sString, string sSep=" ") {
    int nCount = GetStringSubStrings(sString, sSep);

    return GetStringSubString(sString, Random(nCount), sSep);
}

struct SubString GetFirstSubString (string sString, string sSep = " ") {
    struct SubString ret;
    int nSep = FindSubString(sString, sSep);

    if (nSep < 0) {
        ret.first = sString;
        ret.rest  = "";

        return ret;
    }

    ret.first = GetStringLeft(sString, nSep);
    ret.rest  = GetStringRight(sString, GetStringLength(sString) - (nSep + GetStringLength(sSep)));

    return ret;
}

struct StringList GetStringList (string sString, string sSep=" ", int nLimit=16) {
    int nCount = 0, nSep = 0, nStart = 0;
    int nSkip = GetStringLength(sSep);
    int nLen = GetStringLength(sString);
    struct StringList sl;

    if (nSkip < 1) {
        sl.size = 0;
        return sl;
    }

    while (nSep < nLen && nCount < nLimit) {
        nSep = FindSubString(sString, sSep, nStart);
        if (nSep < 0)
            nSep = nLen;

        switch (nCount & 1) {
            case 0: switch (nCount++) {
                case  0: sl.s0  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  2: sl.s2  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  4: sl.s4  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  6: sl.s6  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  8: sl.s8  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case 10: sl.s10 = GetSubString(sString, nStart, (nSep - nStart)); break;
                case 12: sl.s12 = GetSubString(sString, nStart, (nSep - nStart)); break;
                case 14: sl.s14 = GetSubString(sString, nStart, (nSep - nStart)); break;
            }
            break;

            case 1: switch (nCount++) {
                case  1: sl.s1  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  3: sl.s3  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  5: sl.s5  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  7: sl.s7  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case  9: sl.s9  = GetSubString(sString, nStart, (nSep - nStart)); break;
                case 11: sl.s11 = GetSubString(sString, nStart, (nSep - nStart)); break;
                case 13: sl.s13 = GetSubString(sString, nStart, (nSep - nStart)); break;
                case 15: sl.s15 = GetSubString(sString, nStart, (nSep - nStart)); break;
            }
            break;
        }

        nStart = nSep + nSkip;
    }

    sl.size = nCount;

    return sl;
}

struct IntList GetIntList (string sString, string sSep=" ", int nLimit=10) {
    int nCount = 0, nSep = 0, nStart = 0;
    int nLen = GetStringLength(sString);
    int nSkip = GetStringLength(sSep);
    struct IntList il;

    if (nSkip < 1) {
        il.size = 0;
        return il;
    }

    while (nSep < nLen && nCount < nLimit) {
        nSep = FindSubString(sString, sSep, nStart);
        if (nSep < 0)
            nSep = nLen;

        switch (nCount++) {
            case  0: il.i0  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  1: il.i1  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  2: il.i2  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  3: il.i3  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  4: il.i4  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  5: il.i5  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  6: il.i6  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  7: il.i7  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  8: il.i8  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
            case  9: il.i9  = StringToInt(GetSubString(sString, nStart, (nSep - nStart))); break;
        }

        nStart = nSep + nSkip;
    }

    il.size = nCount;

    return il;
}

Funky

Modifié par FunkySwerve, 01 juillet 2011 - 04:50 .


#4
CID-78

CID-78
  • Members
  • 1 124 messages
some of those look quite awful. fixed size arrays is quite pointless you could use a regular struct with propper variable names for that. and when you want to get data you either loop over the string or get the right data directly. depending on your pseudo array approach, switch/case is pointless, you can just put the lines after each other if you want to push them into a struct.

#5
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
You don't know what you're talking about.
a) None of those are fixed length arrays. Some of them are limited in the number of array components. If you think that's pointless, your argument is not with me, but with the professional coders of the world. Think resref (16), VARCHAR (255), and so on.
B) The unlimited pseudoarray function there is GetFirstSubString. The fact that we have not just it but others should really have tipped you off that the others have some separate utility. By way of example, here's some code that uses both limited and unlimited arrays - an array of arrays, space-separated item property strings:

    if (nAddCount) {

        sAddedDescription = "=====\\nEnchantment Quantity: " + GetEnchantmentQuantity(nAddCount) + " (+" +
                                 IntToString(nAddCount) + ")\\n" + C_LIGHT_GREEN + "Added:\\n";
        for (ss = GetFirstSubString(sAdded, " "); ss.first != ""; ss = GetFirstSubString(ss.rest, " ")) {

            il = GetIntList(ss.first, ",");
            if (GetIsItemPropertyStacking(il.i0, il.i1)) {

                if (il.i0 == ITEM_PROPERTY_ARCANE_SPELL_FAILURE){

                    //do special asf encoding
                    if (il.i3 < 10) {
                        nX = 10-il.i3;//encode 'asf good 1-10)
                        AddLocalInt(oItem, "RIPStack_84_0", nX);
                    }
                    else {//fake subtype for bad spell fail prop
                        nX = il.i3-9;//encode 'asf bad 1-10)
                        AddLocalInt(oItem, "RIPStack_84_1", nX);
                   }
                }
                else
                    AddLocalInt(oItem, "RIPStack_"+IntToString(il.i0)+"_"+IntToString(il.i1), il.i3);

            } else {


                sAssemble = IntToString(il.i0)+","+IntToString(il.i1)+","
                            +IntToString(il.i2)+","+IntToString(il.i3)+","
                            +IntToString(il.i4)+","+IntToString(il.i5);

                sTempString = GetLocalString(oItem, "TempPropString");
                if (sTempString != "")
                    sTempString += " ";

                sTempString += sAssemble;
                SetLocalString(oItem, "TempPropString", sTempString);
            }

            if (GetIsItemPropertyImmunity(il.i0))
                il.i3 = GetCostValueFromPercentImmune(il.i3);

            sAddedDescription += C_LIGHT_GREEN + "  " + GetItemPropertyInfoDirect(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5) + "\\n";
        }
    }

Funky

Modifié par FunkySwerve, 02 juillet 2011 - 09:17 .


#6
Shadooow

Shadooow
  • Members
  • 4 465 messages
Anyway OP want to make a chat based conversation with key words aka morrowind or where it was? maybe in Wizardry.

And I cant see any good algorhytm to make it, thats why I sent him there. Does anyone have any idea?

#7
CID-78

CID-78
  • Members
  • 1 124 messages
you can simply use space as a delimiter and check each word against a keyword list. and execute accordingly.

funky:

No your adding a unnecissary layer on your system, that isn't the optimal way of doing arrays, especially not if you want to emulate how arrays work in C and C++. It also make the code harder to read.

you want to work as close to the memory as you can. and you want to have clean code. The only reason i can see todo things your way is if you got alot of similar structs but with diffrent names, and you want to save some code space. Normally you want to work directly towards the pseudostring and not against an struct which you can't build loop with.

if you skip the struct layer you got what you need.

#8
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

ShaDoOoW wrote...

Anyway OP want to make a chat based conversation with key words aka morrowind or where it was? maybe in Wizardry.

And I cant see any good algorhytm to make it, thats why I sent him there. Does anyone have any idea?

Just a space-separated string using FindSubString or one of the functions I provided will do the trick. I'd elaborate, but without knowing more specifics, any number of ways could be best. You could allow multiple acceptable passwords with FSS != -1, for example, or randomly changing passes with GetRandomSubString, or any number of other possibilities.

Funky

#9
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
I really agree with Funky, in that we need more info on  what he is designing, to give meaningfull feed back.

If it is just a single NPC that this chat is happening with, The old set listening pattern could do away with the need for arrays. 

If it is a module wide system using On PC chat event. Then the packed string sounds like a good option.  

But more information on the system is needed.

#10
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

CID-78 wrote...
funky:

No your adding a unnecissary layer on your system, that isn't the optimal way of doing arrays, especially not if you want to emulate how arrays work in C and C++. It also make the code harder to read.

You just called them fixed length arrays, and now you want to lecture me on optimization? Lol. First of all, this isn't my code. It was written by a professional - acaos. Secondly, I never said it was the 'optimal' way to do arrays. Without pointers, I'm not really sure that any one way is much better than another, frankly. Those functions were NOT, however, written with only speed in mind, but also convenience. The more you code, the more you realize that the 'best' code is not just the fastest - there are many other concerns to consider, like modularily, readability (and they most certainly do NOT make the code harder to read, they make it easier - you yourself mention saving on code space below), and convenience. And, while we're at it, part of the usefulness of those functions IS PRECISELY that you can treat arrays in more C-like fashion - by pointing directly to a member of the array in your code. See, e.g., the code I posted above.

If you want to run some speed tests against your method, by all means, have at it - the differences were talking about are miniscule, for considerable gains in the other departments.

you want to work as close to the memory as you can. and you want to have clean code.

The concept of working close to the memory is sort of inane when applied to nwscript. It's not like we're doing mallocs or worrying about packing our arrays into nice tidy sections here. Maybe with nwnx plugins. As for clean code, that's part of the motivation for these functions, as I noted above.

The only reason i can see todo things your way is if you got alot of similar structs but with diffrent names,

No, not just structs. These are pseudoarray functions. IntList, by way of example, is used all over the place in our mod, for schlepping up numerical data in string into ready-to-use format. It can handle an enormous variety of things, from dynamically applied var-based weapon onhits (yes, it's quite fast enough for that, and then some), to our quest-system data, to parsing chat commands with multiple numeric inputs. I'm getting the impression that you really don't understand how we're using these, so here's another example (var-based weapon onhits able to apply any effect/s with just about any conditions - they're used for nearly all our npcs' onhits and for a fair number of pc-based abilities, all running from a single script, the intelligent weapon onhit):

/*
 * OnHitDamages format:
 *   TYPE,DICE,SIDES,BONUS[,RACEMASK]
 *
 * OnHitEffects format:
 *   DYNEFFECT[|CHECK[|CHECK...]]
 *
 * DYNEFFECT format:
 *   LINKEFFECT[;LINKEFFECT...][:SECONDS[+RANDOM]][:E|S]][/VFX][#SPELLID]
 *
 * LINKEFFECT format:
 *   EFFECTID[,PARAM...]
 *
 * CHECK format:
 *   CHECKTYPE:PARAM[:PARAM...][/NAME]
 *
 * CHECKTYPES
 *   1 - percent chance (param is percentage chance)
 *   2 - saving throw (params are type, dc, random dc, subtype, feedback)
 *   3 - ability check (params are ability, dc, random dc, feedback, auto)
 *   4 - skill check (params are skill, dc, random dc, feedback, auto)
 *   5 - favored enemy check (param is a favored enemy mask)
 *   6 - spell resistance check (param is spell penetration)
 *
 *   & - last check was successful (and)
 *   ^ - last check failed (exclusive or)
 *
 * OnHitBreach (integer)
 *   if > 0 will breach that many spells
 *   if < 0 will act as a dispel of level = abs(OnHitBreach)
 *
 */

*snip*

    /* apply special on-hit effects */
    if (sEffects != "") {
        int nPos, nDurType, nSubType, bPetrify, bSelf, bLast = FALSE;
        float fDur;
        string sDur;
        effect eLink;
        struct IntList il;
        struct SubString css;

        ss.rest = sEffects;

        /* parse each effect in turn */
        while (ss.rest != "") {
            ss  = GetFirstSubString(ss.rest);
            css = GetFirstSubString(ss.first, "|");

            /* if the effect has any pre-effect checks (e.g. random chance, saving throw),
             * make them before applying the effect */
            if (css.rest != "") {
                ss.first = css.first;

                if (css.rest == "&") {
                    if (!bLast)
                        continue;
                } else if (css.rest == "^") {
                    if (!(bLast = !bLast))
                        continue;
                } else {
                    bLast = !(CheckDynamic(css.rest, oTarget, OBJECT_SELF) > 0);

                    /* CheckDynamic returns TRUE if they pass; we invert it above so
                     * bLast is 'did the last effect go through' */
                    if (!bLast)
                        continue;
                }
            }


            if ((nPos = FindSubString(ss.first, "#")) >= 0) {
                nSpellId = StringToInt(GetStringRight(ss.first, GetStringLength(ss.first) - (nPos + 1)));
                ss.first = GetStringLeft(ss.first, nPos);

                if (GetHasSpellEffect(nSpellId, oTarget))
                    continue;
            } else
                nSpellId = -1;


            /* check if the effect has an associated visual */
            if ((nPos = FindSubString(ss.first, "/")) >= 0) {
                nVis     = StringToInt(GetStringRight(ss.first, GetStringLength(ss.first) - (nPos + 1)));
                ss.first = GetStringLeft(ss.first, nPos);
            } else
                nVis = 0;


            nSubType = 0;
            bPetrify = FALSE;
            bSelf    = FALSE;

            /* check if the effect has a duration parameter specified; if so, apply it;
             * otherwise, determine the duration of the effect automatically */
            if ((nPos = FindSubString(ss.first, ":")) >= 0) {
                sDur     = GetStringRight(ss.first, GetStringLength(ss.first) - (nPos + 1));
                ss.first = GetStringLeft(ss.first, nPos);

                /* check if a subtype was specified */
                if ((nPos = FindSubString(sDur, ":")) >= 0) {
                    string sSub = GetStringRight(sDur, 1);

                    if (sSub == "E")
                        nSubType = SUBTYPE_EXTRAORDINARY;
                    else if (sSub == "S")
                        nSubType = SUBTYPE_SUPERNATURAL;

                    sDur = GetStringLeft(sDur, nPos);
                }

                if ((nPos = FindSubString(sDur, "+")) >= 0) {
                    int nRandDur = StringToInt(GetStringRight(sDur, GetStringLength(sDur) - (nPos + 1)));

                    fDur = StringToFloat(GetStringLeft(sDur, nPos)) + Random(nRandDur);
                } else
                    fDur = StringToFloat(sDur);

                if (fDur < 0.0) {
                    fDur = 0.0;
                    nDurType = DURATION_TYPE_PERMANENT;
                } else if (fDur == 0.0)
                    nDurType = DURATION_TYPE_INSTANT;
                else
                    nDurType = DURATION_TYPE_TEMPORARY;


                int bFirst = TRUE;
                struct SubString sss;
                sss.rest = ss.first;

                while (sss.rest != "") {
                    sss = GetFirstSubString(sss.rest, ";");
                    il = GetIntList(sss.first, ",");

                    if (bFirst) {
                        bFirst = FALSE;
                        eLink  = EffectDynamic(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5, il.i6);
                    } else
                        eLink = EffectLinkEffects(eLink, EffectDynamic(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5, il.i6));
                }
            } else {
                il = GetIntList(ss.first, ",");

                if (il.i0 < 100) {
                    fDur     = 6.0;
                    nDurType = DURATION_TYPE_TEMPORARY;
                } else {
                    fDur     = 0.0;
                    nDurType = DURATION_TYPE_INSTANT;
                }

                if (il.i0 == EFFECT_TYPE_HEAL)
                    bSelf = TRUE;
                else if (il.i0 == EFFECT_TYPE_PETRIFY)
                    bPetrify = TRUE;

                eLink = EffectDynamic(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5, il.i6);
            }

            if (bSelf) {
                if (GetCurrentHitPoints(oCaster) < GetMaxHitPoints(oCaster)) {
                    if (nVis > 0)
                        ApplyVisualToObject(nVis, oCaster);

                    ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oCaster);
                }
            } else {
                if (nVis > 0)
                    ApplyVisualToObject(nVis, oTarget);
                else if (nVis < 0)
                    eLink = EffectLinkEffects(eLink, EffectVisualEffect(-nVis));

                if (nSubType == SUBTYPE_EXTRAORDINARY)
                    eLink = ExtraordinaryEffect(eLink);
                else if (nSubType == SUBTYPE_SUPERNATURAL)
                    eLink = SupernaturalEffect(eLink);

                if (nSpellId >= 0)
                    SetEffectSpellId(eLink, nSpellId);

                if (bPetrify)
                    ApplyPetrifyEffect(nDurType, eLink, oTarget, fDur);
                else
                    ApplyEffectToObject(nDurType, eLink, oTarget, fDur);
            }
        }
    }


Normally you want to work directly towards the pseudostring and not against an struct which you can't build loop with.

Again, I'm not sure you understand how GetFirstSubString works. It uses a struct with two strings to loop through a given string evaluating substrings. Check the comments above it.


Funky

Modifié par FunkySwerve, 02 juillet 2011 - 02:16 .


#11
CID-78

CID-78
  • Members
  • 1 124 messages
let just say we have diffrent opinions on what's the best approach. I allways go for speed. And I do know what i am talking about, perhaps i don't get the whole message across to you. I am a computer engineer and I know how a computer and compilers works. The above seem to have been written by a high level programmer. Which have been things more like a class and waste resources instead of writting a clean interface. I don't say that he don't know what he do/did,

I just say that you can do it better if you skip structs, because you rarely need structs in nwscript.
they are certainly not good for lists. which is the most common use for arrays. and list are often used in loops where you only need to see one entry or two at a time. not the whole array at the same time

and when i talk about as close to memory i obviously mean as close that you can get in nwscript ie. the string itself. the more you nest and build layer the slower will each operation become.

#12
Khuzadrepa

Khuzadrepa
  • Members
  • 188 messages
Can you give an example of how you would do it, CID-78? I'm trying to follow your logic, but it would be easier with something to compare against the code Funky provided.

#13
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Khuzadrepa wrote...

Can you give an example of how you would do it, CID-78? I'm trying to follow your logic, but it would be easier with something to compare against the code Funky provided.


I'll second that. I'm especially curious to see how you write a function with GetFirstSubString's functionality without using a struct.

Funky

#14
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

Khuzadrepa wrote...

Can you give an example of how you would do it, CID-78? I'm trying to follow your logic, but it would be easier with something to compare against the code Funky provided.


I will try and answer this.  I hop CID does not ming me butting in. 

Most of the work for paeceing a string has alrady been done for you in the x3_inc_string include.

here is a simple example for adding a get first/next functionality to it.   allong with a simple script to show/test the use.  Of cource it would be better to just make the top two functions along with the one declaired global string as an include file.

#include "x3_inc_string"

string sParse; // This string if for use by GetFirst/Next only
                          // It is a place holder for the itorator.
                          // it is global to both functions.

// sArray is a string with space delimited tokens as array elements.
string GetFirstToken(string sArray)
{
   string sToken = StringParse(sArray);
   sParse = StringRemoveParsed(sArray,sToken);
   return sToken;
}


// the token string used by this function is the one  set in the GetFirstToken function. 
//therefore it has no arguments.
string GetNextToken()
{
   return GetFirstToken(sParse);
}
 
 

void main()
{
  string Array = "Red Blue Green";
   float fDelay=1.0;
 
string sSpeak =   GetFirstToken(Array );
  while (sSpeak != "")
  {
     DelayCommand(fDelay,SpeakString(sSpeak));
     fDelay += 1.0;
     sSpeak = GetNextToken();
  }
}



Edited to correct spacing.

Modifié par Lightfoot8, 02 juillet 2011 - 06:17 .


#15
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Lightfoot8 wrote...

I will try and answer this.  I hop CID does not ming me butting in. 

Most of the work for paeceing a string has alrady been done for you in the x3_inc_string include.

Those functions are essentially the same as the ones I posted...though not as cleanly written (look at the include above, though I think I might've left out our StringReplace function). I'm curious to see what CID is advocating.

Funky

Modifié par FunkySwerve, 02 juillet 2011 - 06:44 .


#16
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

FunkySwerve wrote...

Lightfoot8 wrote...

I will try and answer this.  I hop CID does not ming me butting in. 

Most of the work for paeceing a string has alrady been done for you in the x3_inc_string include.

Those functions are essentially the same as the ones I posted...though not as cleanly written (look at the include above, though I think I might've left out our StringReplace function). I'm curious to see what CID is advocating.

Funky


It is just a simple example.   I wrote it before I seen you post for a request of a GetFirst/Next without use of struct's, Well it uses no structures. The principals behind it can be made as simple or as complex as you like.   I myself do not even think the functions are needed in my example just use of the StringParse and StringRemoveParsed functions in the script where needed would do the trick for parsing through the string.  

Before we really know what the OP is looking for all we are really doing at this point is cluttring up his thread. 

#17
Shadooow

Shadooow
  • Members
  • 4 465 messages

CID-78 wrote...

you can simply use space as a delimiter and check each word against a keyword list. and execute accordingly

FunkySwerve wrote...
Just a space-separated string using FindSubString or one of the functions I provided will do the trick. I'd elaborate, but without knowing more specifics, any number of ways could be best. You could allow multiple acceptable passwords with FSS != -1, for example, or randomly changing passes with GetRandomSubString, or any number of other possibilities.

thats not the full solution, this part is clear even to OP, but what next, how to set up the conversation and key words and how to show them and speak them etc etc.

#18
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
He asked to be 'enlightened' about pseudoarrays. He was. He also mentioned you were helping him. At least two of us have asked for more information before rendering help. If you disagree, or know more than we do about what he wants from speaking with him, how about helping him instead of complaining about us? LF is right, the thread is getting cluttered enough - I'm waiting for him to post.

Funky

#19
TrekSL

TrekSL
  • Members
  • 32 messages
OK guys a big thank you - (I've just got back from work) I'll give you some insight here:

The idea for me is to construct a learning game. The scripting that I intended was to have a simple game that relied on correctly typed phrases to proceed through locked doors for example. The reason behind this was that I could just make all of this in the bog standard clickable dialogue boxes, but that would be far too easy - so I wanted a text parser for the job. ShadoOoW has shown me how and I have adapted the code to suit. However, what I have is so far storing a single phrase. I can expand here, I could use integers or strings to store more of these phrases but, to quote my java teacher 'these are not elegant solutions'. I have just seen the pseudo arrays by funky and lightfoot. You are all very accomplished coders on this forum (I'm just starting out, I wish I had a fraction of the technical know-how you do!!!) but for the time being I do feel intimidated by the complexity of getting it working - so the less elegant may be my best option at the moment!

To continue my example though: Say you have a locked door. You can't open it until you have correctly typed the conjugation of haben in german. In order, these are 'habe' 'hast' 'hat' 'haben' 'habt' 'haben' (Ich, Du, Er, Wir, Ihr, Sie/ I, you, he/she/it, we, you(pl) you(formal))

#20
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Well first off I do not think you need to use an array for this.  Arrays my be great for full languages. in NWN script however they often add a complexity that you do not need. 

Second I see no need to use the OnPlayerChat event.   It looks like that is what you are currently using but also trying to fire the same event from a trigger also.  That just makes any strings you get from the OnPCChat event functions unreliable without a bunch of extra code in the Chat event.   It is often better to stick with what was desined into the game instead of stepping out and desinging a new sub system.  Agreed there are time when you have not choice but to design a new system, but I do not think it is needed in this case.

To me your solution is to let the door listen for the answers useing SetListeningPatern for each of the strings that are a possiable answer to its question. Now at lot of people will chime in here and start saying that it can not be done that way because the door does not have a OnConversation event.  That is where the well hidden part of this comes into play.  even though the door does not have a OnConversation event. you can make one.   When the door is set to listening (SetListening(oDood);)  It will fire the default OnConversation Event Script. nw_g0_conversat.  You make your event by modifing the script to run a UserDefined Event on the door.  making it to where you can use that event to put your code for what the door is listening for and what to do when it hears it.

I outlined that process in the thread titled.SetListening() . . is it only for NPCs? Of cource you will want to change the OBJECT_TYPE_PLACEABLE to OBJECT_TYPE_DOOR or a conjunction of the two according to your needs.

After that you just need to define unique ID numbers for each of you listening patterns to use in the OnUserDefined event for the door. I guess you could do all of that with arrays but I eally do not see the need in nwn script since you would assign them as single patterns anyway.

#21
Shadooow

Shadooow
  • Members
  • 4 465 messages

TrekSL wrote...
To continue my example though: Say you have a locked door. You can't open it until you have correctly typed the conjugation of haben in german. In order, these are 'habe' 'hast' 'hat' 'haben' 'habt' 'haben' (Ich, Du, Er, Wir, Ihr, Sie/ I, you, he/she/it, we, you(pl) you(formal))

then its easy, just store the "multistring" in format ich|du|er|etc| and then get chat message, lowercase it (beware that characters like Ü and other special characters cannot be lowercased!) add "|" and then try if you findsubstring position

like this:

string sPattern = GetLocalString(OBJECT_SELF,"PHRASES");
string sMessage = GetPCChatMessage();
sMessage = GetStringLowercase(sMessage)+"|";
 if(FindSubString(sPattern, sMessage) > -1)
 {
  //one of the correct phrases, lets do some stuff
 }


LF8: The system I provided him contains only 1variable check in OnChat, with additional 3commands if the variable is set and is reliable. Im doing it this way as having a listener when you want to use it on doors/trigger is a bit silly and this way it dont mess with onchat event (its completely the same as tagbased scripting).:wub:

#22
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
I prefer Lightfoot's solution - despite over 25,000 lines of code in our chat scripts (yes, really), we generally rely on listeners for area-, location-, and speaker-dependant things - modularity. I guess the one thing standing in its way is the question: do you want them to have to speak them one after the other, or all at once?

I would be inclined to track progress with a space-separated (or semi-colon, if some of the answers will have spaces) local string on the door itself, if you want them to answer one after the other. Then, you could just get the first substring, compare it to what they said, and snip it off from the later conjugations, or open the door if they're on the last.

There are going to be endless ways to do this, though. If you prefer to use what code Shad has already given you, I can adapt to that.

Also, if you dislike the listening event aspect, and/or want to have a central include which lists all your answers, you could simply tag all riddle doors the same thing, check for GetNearestObjectByTag, to find the nearest door, and mark each door with a unique local int: Door int 1, Door int 2, and so on. Then, in the master include, you could pull up the full answer list simply by use of a switch statment, and track only progress on the door using locals, rather than setting the answer there to begin with.

I understand you're looking for an elegant solution. To me, that may mean something a little different, so let's clarify your goals. For me, I would want:
a) A central include holding all questions and answers - for easier data filling .If you're comfortable with databases, that's what I'd normally suggest if you're using MySQL on a server, since databases are made for this stuff. If you want to be able to readily read and append, though, the bioware database is not so hot, so maybe the best solution is in fact a scripted include, if this is for single player use.
B) Minimal setup on individual doors - drop a door in from the palette, change a single variable, and it's ready
c) Ability to reset/relock doors, and to shuffle questions and answers at random, so that you don't have to make a zillion doors. Heck, maybe even a circle they have to walk around a number of times before a cetral door opens.

So, my questions are, aside from the initial one about how you want the riddles spoken:
1) how many conjugations are we talking
2) how do you want to handle randomization, if at all?
3) how many doors, in what configuration, in how many areas?

Funky

Modifié par FunkySwerve, 03 juillet 2011 - 01:33 .


#23
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Yes, Shadow I remember your system now. It is still a matter of prefferance. I myself find having the door listening more usefull then routing it through the OnChat event. For one there is no way to tell if the door that you want listening is even in range to hear the spoken words without extra code added. Two there is no direct link to the door without adding a search for it within the code.

With the door itself listening there is no question that it heard the PC speak, there is also no need to search for the door since it is OBJECT_SELF the event is running on it after all. there is no problem finding the PC who spoke the words either just use oPC=GetLastSpeaker(); There is no extra code needed to have firm links between the objects in questions. You can also set the entire search set up to one listening patern.

SetListenPattern(oDoor,"habe|hast|hat|haben|habt|haben", 150);

or if the was wanting them all spoken in order on the same line.

SetListenPattern(oDoor,"habe**hast**hat**haben**habt**haben", 150);

or all spoken on the same line with nothing else by white space include.
SetListenPattern(oDoor,"(habe)*w(hast)*w(hat)*w(haben)*w(habt)*w(haben)", 150);

or single checks with multipal checks.
SetListenPattern(oDoor," (habe|erste|ein) **(habe|erste|ein)", 150);

would match things like:..... hmm german is a little more complex. The translator is changing the words on me. lol. let me do this as an english example

SetListenPattern(oDoor," (habe|first|one) **(habe|first|one)", 150);
wold match things like:
The first is habe
and
habe is number one
unfortantly with the way I have it written it would also match.
one is the first.

So yes with the tools availiable with the listening patterns I like it better then channeling through the player chat event. Perhaps that is avaliable in the Chat event also, I dont know. I have not looked that close. Still there is the problem with seperating out what is doing the listening. with the old listening pattern system there is no problem there or even with having several objects listening at the same time, something I do not think your system handles very well.

Just my opinon, We all have our own preferances and it realy does not matter what system the OP picks as long as it works for him and he is happy with it.

#24
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
for setting up your listening patterns here is a quote.

From Noel (Bioware):
** will match zero or more characters
*w one or more whitespace
*n one or more numeric
*p one or more punctuation
*a one or more alphabetic
| is or
( and ) can be used for blocks

- setting a creature to listen for "**" will match any string
- telling him to listen for "**funk**" will match any string that contains the word "funk".
- "**(bash|open|unlock)**(chest|door)**" will match strings like "open the door please" or "he just bashed that chest!" 



#25
Shadooow

Shadooow
  • Members
  • 4 465 messages

Lightfoot8 wrote...

for setting up your listening patterns here is a quote.

From Noel (Bioware):
** will match zero or more characters
*w one or more whitespace
*n one or more numeric
*p one or more punctuation
*a one or more alphabetic
| is or
( and ) can be used for blocks

- setting a creature to listen for "**" will match any string
- telling him to listen for "**funk**" will match any string that contains the word "funk".
- "**(bash|open|unlock)**(chest|door)**" will match strings like "open the door please" or "he just bashed that chest!" 

You got a point Lightfoot8, I totally forgot about SetListenPattern function. Thats especially suitable for this issue. However using this function is really quite complicated as you have to use creature, special onspawn and special OnConversation script. Its the way to do that for sure, maybe even better, but my solution has its advantages too. Well, lets see what will OP prefer now.:innocent: