Trigger not working on creatures (OnEnter...)

#1

I can’t get triggers to activate when creatures enter it…but if I change the condition for PC’s and my character enters it it works. What am I doing wrong?

  void main()
    {
        // Get the creature who triggered this event.
        object oPC = GetEnteringObject();

        // Only fire for creatures.
        if (GetIsPC(oPC))
        {
        return;
        }
        else
        {
        ActionSpeakString("YAHOO");
        }
    }

I even tried this without success:

  void main()
    {
        // Get the creature who triggered this event.
        object oPC = GetEnteringObject();

        // Only fire for creatures.
        if (!GetIsPC(oPC))  
        {
        ActionSpeakString("YAHOO");
        }
    }
0 Likes

#2

You need to define the enterer. Otherwise, there is no one to Speak the String

void main()
{
object oEnterer = GetEnteringObject();
object oSelf = OBJECT_SELF;

and so on

0 Likes

#3

Try just SpeakString(). I suspect your creature has something in its action queue that is still running. You could also try ClearAllActions() before the ActionSpeakString() but that will stop the creature in its tracks.

0 Likes

#4

@meaglyn I tried both suggestions, the creatures didn’t even stop (in my test module they have a waypoint telling them to the go to the trigger)

      void main()
        {
            // Get the creature who triggered this event.
            object oPC = GetEnteringObject();

            // Only fire for creatures.
            if (!GetIsPC(oPC))
            {
            ClearAllActions();
            ActionSpeakString("YAHOO");
            }
        }

@DM_Wise
I figured the enterer is defined by
object oPC = GetEnteringObject();

What am I missing?

0 Likes

#5

You aren’t defining who is to speak the string.
According to your script, the Trigger itself is clearing all actions, and speaking the string.

Try:

AssignCommand(oPC, ClearAllActions());
AssignCommand(oPC, SpeakString(oPC, “Yahoo!”));

This is why it’s not working. Once the entering object is defined, it needs to be assigned the commands, or the script assumes it’s the firing object that should execute the commands. Also, using the Clear AllActions() command will stop the NPC from walking the waypoints. My suggestion is to just have it speak the string because speaking doesn’t sit in the action queue.

1 Like

#6

D’oh. The part about it works for the PC made me gloss over that little issue. Good call Ghool.

0 Likes

#7

First off, thank you. Secondly, after your help I realized I wanted to get this command to work only on “certain” creatures, and I made it work by using the script below (comparing the entering object to the one I want to assign the command to). However, as soon as I add a second creature with the same tag (“Beetle”) and only of those beetles enter the trigger, it doesn’t work. Isn’t that strange?

  void main()
    {
        // Get the creature who triggered this event.
        object oPC = GetEnteringObject();
        object oBeetle = GetObjectByTag("Beetle");


        // Only fire for creatures.
        if (!GetIsPC(oPC))
        {
        if (oPC == oBeetle)
        {
       AssignCommand(oBeetle, SpeakString("Yahoo!"));
         }
    }
}
0 Likes

#8

That’s because GetObjectByTag only returns the first instance unless you specify the second parameter (see Lexicon). If the beetle entering is the second instance, the trigger won’t fire, as it stands.

To fire for all beetles, you need GetObjectByTag:

     void main()
        {
            // Get the creature who triggered this event.
            object oPC = GetEnteringObject();

            // Only fire for creatures.
            if (!GetIsPC(oPC))
              {
                if (GetTag(oPC) == "Beetle")
                 {
                   AssignCommand(oBeetle, SpeakString("Yahoo!"));
                 }
        }
    }

With that script, the trigger will fire every time a Beetle enters, even if one is in the trigger already, or has visited before. That may be sufficient for your purposes.

3 Likes

#9

Actually I don’t want it to fire for all beetles, I just want it to fire to the beetles that enter the trigger, but a quick search how do I get the tag of objects have given me what I needed to I solved the problem with GetTag :slight_smile: Thanks!

  void main()
    {
        // Get the creature who triggered this event.
        object oPC = GetEnteringObject();
        string oBeetle = GetTag(oPC);

        // Only fire for creatures.
        if (!GetIsPC(oPC))
        {
        if (oBeetle == "Beetle")
        {
       AssignCommand(oPC, SpeakString("Yahoo!"));
         }
    }
}
0 Likes

#10

That’s exactly the same as what Proleric posted but less efficient :slight_smile:

1 Like

#11

Solemn word of advice on the side - Proleric’s making an example of two things that are actually rather important, there. Don’t skip past it too quickly; even if it seems like nitpicking, this is important stuff that’s going to bite you in the butt if you neglect it.

Warning - pedantry follows. :wink:



Prefixes in variable names are normally supposed to signify what type of variable it is, for readability and ease of understanding the code. Somebody seeing a variable called “oBeetle” would generally assume that this is an object type of variable, rather than a string. For a string variable, it should be “sBeetle”.

You’ll thank yourself for it later if you adopt a systematic naming and commenting approach, especially when your code gets longer, and when you have to revisit your code after some years, or get helpers or new team members who have to read and alter it.

In the same way, I’d suggest not declaring “oPC” if the definition is not intended to be a player character. oEnter, or even oBeetle, would be less confusing.

One more variant, for the heck of it:

void main()
{
    object oEnter = GetEnteringObject();

    if (!GetIsPC(oEnter) && GetTag(oEnter) == "Beetle")
        AssignCommand(oEnter, SpeakString("Yahoo!"));
}

I think PCs can’t actually have tags, though, so maybe the GetIsPC() check is unnecessary. Not sure about DM-possessed creatures. :open_mouth:



Second thing Proleric pointed out there is another fundamental thing you definitely want to know about. Don’t skip past his advice carelessly. These are the foundations of the craft, and you need to understand them to build solid constructs; rushing past stuff like this will result in shoddy code in the long run, and you and your future teammembers are the people who are going to be suffering for it. I can’t emphasize this enough - do not cheerfully rush past the basics, or make a habit of disregarding any input when it’s about good form and structure. You’re going to wind up constructing a beautiful castle that you love dearly on an unstable foundation, which may easily crumble, as well as alienating the very people whose methodical mindset enables them to complement your more relaxed and creative approach as part of a team.

GetObjectByTag is a function that looks for and returns one object with a given tag. In the first variant, you were declaring oPC as the object that entered this trigger, and oBeetle as the first object in the module that has the tag “Beetle”, then checking whether oPC is oBeetle. This way around, the trigger would only ever have worked for a single beetle at a time, as there would only ever have been a single object in the module that could have passed the “oPC == oBeetle” check.

So, you really do want the trigger to work whenever any beetle enters the trigger, (note how Proleric’s example (checking for the tag of the entering object) is actually the very solution you discovered yourself; he gave you a direct shortcut there). You do not want the reaction of the beetle that enters the trigger to be assigned to all beetles, but you do want any beetle to be recognized by the trigger, qualifying for the reaction.

But: “All beetles reacting at once when a single beetle enters a trigger” isn’t actually an issue to begin with. Within the script, you have only a single object declared and defined to whom the SpeakString command would have been assigned.

    // We declare oPC as being the object that has entered the trigger in this OnEnter event script.
    object oPC = GetEnteringObject();

    // To oPC, a single object previously defined, we assign the command to speak a string.
    AssignCommand(oPC, SpeakString("Yahoo!"));

To make all beetles in the module react when a single beetle enters the trigger, you would have had to cycle through multiple objects, assigning the command to them one at a time, for instance with a while loop:

// We declare an integer i, to have an incrementable number with which to cycle through the beetles.
int i;
// We declare an object oTarget, defining it as the first object in the module that has the tag "Beetle".
object oTarget = GetObjectByTag("Beetle");
// For as long as the object oTarget is still a valid object, we repeat the actions within the while loop.
while (oTarget != OBJECT_INVALID)
    {
    // We assign the command to speak a string to the current object oTarget.
    AssignCommand(oTarget, SpeakString("Yahoo!"));

    // We change oTarget to the next object in the module that has the tag "Beetle", using our integer i incremented by 1.
    // Now, in the next round of the while loop, oTarget will be a different object.
    // First the second, then the third, then the fourth, and so on, until eventually GetObjectByTag will return OBJECT_INVALID,
    // which means that the condition of the while loop (oTarget != OBJECT_INVALID) will no longer be TRUE.
    oTarget = GetObjectByTag("Beetle", ++i);
    }

So that looks like a misunderstanding of the basics there, and one that would likely cause you some grief and confusion going forwards. I hope I’ve explained it decently and not too obnoxiously. :slight_smile:


::tips hat:: Pedantry accomplished.

1 Like

#12

I would’ve replied sooner but weekends I tend to clear my mind…
I’ve already implemented your advice when it comes to prefixes… I just thought the “o” is a cute little addon I’ve seen in some script examples, I didn’t realize it has meaning; and I tend to comment usually after the script works but you’re right I haven’t been doing that much.

But yes, good advices, and thanks for laying it out. :slight_smile:

And I completely missed the fact that Proleric used GetTag like I did, I thought Proleric misunderstood me. Oopsie! But yea, the last script I did works, as well as Proleric’s script. Of course, I have other issues with the script which I’m trying to solve… but I wouldn’t like to hijack my own thread and I’ll try to figure it out myself before crying to the experts. :slight_smile:

Much obliged!

-Val

Edit: Typos all over!

0 Likes

#13

I know this topic is for peoples new to scripting but I have to ask. Apart from historical reasons, why is Hungarian notation the convention in NWN scripting? The engine doesn’t have that many types. If we dispense with the variable type prefixes we could really clean up.

0 Likes

#14

What exactly do you mean by “cleaning up”? I completely disagree with your statement. For me personally (and I’m sure, for other people too), It makes it hard to read other’s nwscript code, especially when you’re dealing with custom functions.

0 Likes

#15

It doesn’t (if you don’t count structs), but variables of these few types are often used to express related data. And you want to avoid confusion, not add to it. In other words, in addition to distinguishing between types, prefixes also allow “joining” them logically. Say, oTarget and lTarget: target and its location. It’s actually more clean, because there is really just one word here: Target.

1 Like

#16

That’s actually the perfect example, NWShacker. We have oTarget and lTarget. So what is sTarget? Is it the name? The Tag? The object encoded as a string? The whole flaw in Hungarian notation or at least basic Hungarian is the type isn’t very useful information. It only gives us a broad sense of what we can do with it, not what we should do with it.

A superior prefixing system would be the Apps Hungarian, where the prefix describes the kind of information in the variable. tagTarget tells us exactly what we should do with the variable, as well as allowing us to infer the type.

Of course I’m against Hungarian altogether, but I’m coming from a post Microsoft backlash world. We have the luxury of instantly knowing our types on hover there and descriptive variable names are prescribed.

0 Likes

#17

This is unnecessary overcomplication given the relatively small scale of NWN scripting projects and small amout of built-in data types. Although I agree with your arguments against the Hungarian notation in general. It’s not perfect, but it’s better than nothing.

0 Likes

#18

It can be either of them or all of them at once. All you know that it’s a string related to Target. When you’re the code author that’s usually enough to make a working code you can later understand. Calling a variable tagTarget severely limits it’s usability outside tag area.

Don’t get me wrong, working a lot with loosely-typed languages, I understand your sentiment against HN, but it probably works best with what we have (toolset’s script editor isn’t the most feature-rich IDE around).

To each their own, I find CamelCase way more annoying.

0 Likes

#19

Keep in mind that coding style isn’t standardized throughout NWN scripting, before getting into passionate debates on the finer points. There are too many people involved, working on too many different projects, most of which keep their own set of preferences. One of the bigger selling points with with NWScript is that it’s an opportunity for complete beginners to get into coding in a relaxed way, playing rather than, per se, working.

I think I’d argue that, learning-wise, it’s very easy for someone who’s learned scripting being accustomed to a data-type-based notation style to switch to a more specific one, which isn’t necessarily the case the other way around. On average, you aren’t talking to people who are likely to already know by heart that tag and resref and description are all most likely to be strings, or should at least not assume this to be the case.

Data type prefixes can clear confusion of that kind up in an instant. The “What kind of peg shape is this?”-view makes it easier to understand how to set up custom functions with one’s own parameters, as well.

There also are some occasions every now and then where type-based prefixes make sense even so, like if you need string and float versions of an integer for some reason; “iResult & sResult & fResult”. Multiple IntToStrings and IntToFloats etc can be a little annoying sometimes. Plus (as NWShacker also noted with usability outside of the specified area), sometimes they’re reusable for different purposes, like just plain old i, where you don’t actually need to know a specific purpose for the variable, just what kind of variable it is.

Updating the toolset to show data types on mouseover or highlight would be nice, though. If we had that, I’d agree in an instant that we should be advocating purpose-of-variable-based naming when the opportunity shows itself.

1 Like

#20

Yeah, the convention as is is fine and somewhat useful. You are free not to use it in scripts you write of course. I don’t use it all the time either.

Looking forward to seeing things like tagTag instead of sTag :slight_smile: Putting the meaning of the variable in place of the type hint is not helpful. That’s what the variable name and sometimes context is for.

0 Likes