LucasForums

LucasForums (http://www.lucasforums.com/index.php)
-   Holowan Laboratories (http://www.lucasforums.com/forumdisplay.php?f=324)
-   -   Qui-Gon's Script Shack (http://www.lucasforums.com/showthread.php?t=208456)

Qui-Gon Glenn 09-21-2011 05:11 AM

Qui-Gon's Script Shack
 
Well, this has been a long time in coming, and this is not really how I thought I would get it started, but oh well here we go. This first post will be heavily edited, so for today we will just start with the basic idea of the thread.

The Premise
-------------------------------------------------------------

I like to dabble in things, modding wise. I am not especially talented in any of them, but I guess I like the challenge of scripting, so that is where I find myself most of the time when involving myself in Holowan-style activities.

There are some tutorials on scripting that are quite excellent, and I have some example oriented add-on tutorials half-written for some of the superb work of tk102 and stoffe. However, scripting can be a very dry or confusing subject, and so I have always wanted to have an area where I could work out loud on the scripts I am working on, and also share smart scripts that I find or make that might be helpful. It is hopefully also a place where I can learn a few things, from the masters as well as the rooks.

Also, this is a place to ask for help with a script! Please though, do me a little courtesy and don't just turn this into a script request thread - I am not that skilled, and I have better things to do. However, if you are confused by something, or are having a hard time working something out, or are just stuck - share what you have, and we can try to work through it together. If I can't get it, I will bother some of my scripting mentors enough that maybe they will post a clue!

As I said, this post will be edited a great deal. I am starting it right now because I feel guilty to be hijacking j7's tinkering's thread :p

That said, the first post for now shall contain the recent little script I am working on for Force Fashion K1. I think it is a useful template for giving specific items according to class, and anyone can use it - I don't expect a credit in your ReadMe (unless I actually make new scripts for ya o_Q ) so feel free to use and alter and *improve* at will!

Quote:

Originally Posted by Myself the Hijacker (Post 2791531)
Just thought I would share this in this thread, since it is active and it does pertain to part of FFK1...

or I was just trying to be a shiny object. ooooooh :lol:

This might be a handy script for many folks, so I am sharing it here as a generic script that you can cut & paste, with simple variable name substitution. This will spawn specific items in a container according to a PC's class.

Code:

// qg - to spawn class-specific clothing to a container
//      9/20/2011

/* int CLASS_TYPE_SOLDIER      = 0;
  int CLASS_TYPE_SCOUT        = 1;
  int CLASS_TYPE_SCOUNDREL    = 2;
  int CLASS_TYPE_JEDIGUARDIAN  = 3;
  int CLASS_TYPE_JEDICONSULAR  = 4;
  int CLASS_TYPE_JEDISENTINEL  = 5;
  int CLASS_TYPE_COMBATDROID  = 6;
  int CLASS_TYPE_EXPERTDROID  = 7;
  int CLASS_TYPE_MINION        = 8; */

void main()
{
  object oPC=GetFirstPC();
  object oContainer=GetObjectByTag("containertag");
  int iClass=GetClassByPosition(1,oPC);

  switch (iClass)
  {
      case 0:
        CreateItemOnObject("soldier",oContainer,1);
        break; 
      case 1:
        CreateItemOnObject("scout",oContainer,1);
        break;
      case 2:
        CreateItemOnObject("scoundrel",oContainer,1);
        break;
  }
}

So, there is a general example of what is going on here. I will be reserving the first post after this one, for the possibility that something actually happens here and there is a need down the road for a little organization.

Happy Modding :thumbsup:

Credits: tk102, stoffe, beancounter, RedHawke, DarthStoney, bead-v, Varsity Puppet, Pavlos, Tupac Amaru and JdNoa - I learned it from watching you, ok?!?!?!!!

Qui-Gon Glenn 09-21-2011 05:11 AM

Holowan Scripting Tutorials -If you don't have a clue, start here. It will take a minute :p
The NWNLexicon -a deep resource that is not 100% kotor/TSL safe, but I mean a deep resource and forums and sample code and.......
DeadlyStream -Home to many of the best active KotOR scripters
...

Qui-Gon Glenn 09-21-2011 05:28 AM

The current script I am working on is related to an earlier script I did for j7 when we first were spawning his fantastic Sith Stalker skin. We had fun making a baby mini-mod out of it, with some basic cinematics.

The gist - this script will make folks "appear from nowhere" just uncloaking in your face... and I mean IN YOUR FACE, as they will attack you immediately.

The script will not do it all alone - your NPC would need to be spawned in however you chose and given the effect of invisibility (real easy, just add in EffectInvisibility(oYourNPC) to the spawn script), and then s/he or they (plural FTW :devsmoke:) would need a custom OnSpawn script allowing for perception to be opened. It is a simple cut&paste of the script k_def_spawn but with the "//" removed in front of the line for OnPerception events. Finally my handy script goes into the OnPerception slot of your .utc.

I post this script, for two reasons. One, I know it works and can be used to do several different jobs with just minor alteration. Two, I hope that someone that has used TimBob12's Populate Function will go ahead and use this custom UserDefined script for a .utc of your choice and populate the hell out of 'em. Then go ahead and warp into that area and it's a total free-for-all, but enemies will only appear as you near them.

The obvious test room would be the Rakatan Prison module.... Combining the two makes stealthy angry Mandalorians or Sith A.... Sneaky sith people :p appear wherever you go!

Custom OnSpawn:
Code:

//:: k_def_spawn01
//:: v1.0, Default On Spawn In
//:: Created By: Preston Watamaniuk, Copyright (c) 2002 Bioware Corp.

#include "k_inc_generic"
#include "k_inc_debug"

void main()
{
// WALK WAYPOINT BEHAVIORS (Comment In or Out to Activate ) ****************************************************************************
/* I cut this section out as in this use of the script it is not necessary.
  If you need to have these behaviors in for a spawn, simply use the original
  provided in scripts.bif. As it is, this script will compile and work
  just fine for this purpose, just not all purposes  */


// CUSTOM USER DEFINED EVENTS

    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_HEARTBEAT);        //OPTIONAL BEHAVIOR - Fire User Defined Event 1001
    GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_PERCEPTION); //BEHAVIOR WE NEED - Fire User Defined Event 1002
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_ATTACKED);        //OPTIONAL BEHAVIOR - Fire User Defined Event 1005
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_DAMAGED);          //OPTIONAL BEHAVIOR - Fire User Defined Event 1006
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_DISTURBED);        //OPTIONAL BEHAVIOR - Fire User Defined Event 1008
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_COMBAT_ROUND_END); //OPTIONAL BEHAVIOR - Fire User Defined Event 1003
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_DIALOGUE);        //OPTIONAL BEHAVIOR - Fire User Defined Event 1004
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_DEATH);            //OPTIONAL BEHAVIOR - Fire User Defined Event 1007
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_DISTURBED);        //OPTIONAL BEHAVIOR - Fire User Defined Event 1008
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_BLOCKED);          //OPTIONAL BEHAVIOR - Fire User Defined Event 1009
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_FORCE_AFFECTED);  //OPTIONAL BEHAVIOR - Fire User Defined Event 1010
    //GN_SetSpawnInCondition(SW_FLAG_EVENT_ON_DIALOGUE_END);    //OPTIONAL BEHAVIOR - Fire User Defined Event 1011

// DEFAULT GENERIC BEHAVIOR (DO NOT TOUCH) *****************************************************************************************

    GN_SetDayNightPresence(AMBIENT_PRESENCE_ALWAYS_PRESENT);

    GN_SetListeningPatterns();  //This function although poorly named sets up the listening patterns and other important data for the
                                //creature it should never be removed.
    GN_WalkWayPoints();
}

Custom UserDefined:
Code:

// qg_ambush.nss
// 9-21-2011


void main()
{
  object oSelf = OBJECT_SELF;
  effect eUncloak = EffectVisualEffect(8001);
  effect eCurrent = GetFirstEffect(oSelf);
  int nEvent = GetUserDefinedEventNumber();  // OnPerception
  //////////////////////////////////////////////
 
  if (nEvent == 1002)
  {
      while (GetIsEffectValid(eCurrent))     
      {
        if (GetEffectType(eCurrent) == EFFECT_TYPE_INVISIBILITY)   
        {
            DelayCommand(1.5, RemoveEffect(oSelf, eCurrent));
        }
        eCurrent = GetNextEffect(oSelf);
      }
      DelayCommand(2.0, ApplyEffectToObject(0, eUncloak, oSelf));
      ChangeToStandardFaction(oSelf, 1);
      ExecuteScript("k_ai_master", oSelf, 1005);
  }
}


Warlord664 09-21-2011 07:31 AM

LOL! Awesome. I was just thinking of starting a thread last week that was "all" script questions but I decided that was a bad idea.

very cool.

VarsityPuppet 09-22-2011 10:10 AM

Here's a fun script structure I use quite often.

The Cutscene Script!

The basic premise behind it is that for dialogs and cutscenes, there are multiple nodes where things are going on. Instead of creating a script for each one of these nodes, you can use the slots in the dialog to specify a script, and then input a script parameter to choose which section to use.

Code:

void main(){

        //Declare all of your variables here. You can give them values in each separate case in the switch below

        //Each dialog node allows you to put in 5 pieces of information to deliver to the script.
        //5 integers, with which you can do whatever

        int Param1 = GetScriptParameter(1);
        int Param2 = GetScriptParameter(2);
        int Param3 = GetScriptParameter(3);
        int Param4 = GetScriptParameter(4);

        //and one String parameter, useful for putting in item templates or waypoints or whatever else
        string sParam = GetScriptStringParameter();

        //For the generic Cutscene script structure though, I just use Param1 to build each case
        switch(Param1){
                case 0:
                        //Code goes here
                break;
                case 1:
                        //Code goes here
                break;
                case 2:
                        //Code goes here
                break;
                case 3:
                        //Code goes here
                break;
        } //Keep adding cases and breaks ad nauseum
}

That's the general structure of the Cutscene Script. Then in the dialogeditor where you want it to run, you type the script name and the case you want to run.

Example:

[a_905sleep] [1] [ ] [ ] [ ] [________]

This would run case 1 from the script above.

Hope that was helpful.

Qui-Don Jorn 09-22-2011 10:41 AM

^^ Why, yes.....yes it was.

Qui-Gon Glenn 09-22-2011 04:14 PM

Quote:

Originally Posted by VarsityPuppet (Post 2791621)
Hope that was helpful.

:D

Exactly the stuff we are looking for here! Thanks VP, I think that ultimately the second post will be a directory of the scripts made and contributed here, with a link to the specific post and code. This way, this can be a script depository, and save some searching "everywhere" by just searching here.

Just for clarity, is that a script that will run in either KotOR environment, or is it TSL specific?

VarsityPuppet 09-22-2011 05:32 PM

TSL specific actually... Forgot to mention that.

I'll try to add any scripts I can think to add here. Most of them I just make up on the fly, but I always use this for the cutscenes.

Hmm, I suppose I could show how to make the generic onEnter script or maybe a trigger. I'll get around to it.

Qui-Gon Glenn 09-23-2011 02:59 AM

The generic OnEnter is covered in the tut's, more or less, but if you want to add something to the original tut, feel free. That was another intention of mine: bringing some of the tut's to life a little better by explaining smaller details the original author's left out :)

A trigger would be welcome ;)

Fallen Guardian 09-23-2011 09:56 PM

I actually have a question, if you were to have a party of say: One custom party member and the other the player's choice and you wanted to have a script that relates to the player's choice party member is there a way to generalize the party member instead of having to make separate scripts for each one?

Qui-Gon Glenn 09-23-2011 10:41 PM

Well, I am not sure that I understand the question fully, without a little more background. What I think you are saying is that the non-custom party member you that is the PC's choice, you would like to be able to address this NPC generally? That is too simple I think to be what you are looking for, but the solution sure would be easy - say something generic!

If you are wanting to initiate a different dialog for each standard party member as the the PC choice, that can also be done in one script, although the script will be specific and not general regarding the party member NPC.

Looking through the Lexicon, I am finding some functions for identifying and acting on party members, but many of them are NWN specific and not available in KotOR scripting, or at least not K1. That is unimportant though, we can work around it.

So... I think I need a little more description Fallen!

Fallen Guardian 09-23-2011 11:07 PM

Well this actually relates to the walking script that I posted in my scripting mishap thread. Basically, there is one party member in your party who is mandatory. The other party member is the PC's choice. Now, if I wanted to make a script that would make the PC's choice party member walk to a certain location would I have to make separate scripts for each of the possible party member scenarios or just one that identifies anyone traveling as a henchman of the PC?

zbyl2 09-24-2011 07:19 AM

@Fallen Guardian
If I understand your problem correctly, GetPartyMemberByIndex() is your help here. Function defines party member as object, but you don't have to give tag or anything, it'll work on whoever is party member at a time.
Code:

object oPC = GetPartyMemberByIndex(0);
object oNPC1 = GetPartyMemberByIndex(1);
object oNPC1 = GetPartyMemberByIndex(2);

The int parameter defines which party member it is; 0 is player character, 1 and 2 are other party members. Now only thing you need to figure out which one is 1 and which one is 2, but that shouldn't be too hard ;)

Fallen Guardian 09-24-2011 09:47 AM

Alright, thanks Zybl.

Qui-Gon Glenn 09-26-2011 03:42 AM

Ok Fallen, I am still not sure what the problem was with our earlier stuff, so forgive me for just cut & pasting it here. Perhaps you have figured out what the problem was with the door itself.

While zbyl2 has presented you with a solid means to skin the cat, and one that I was not aware existed :p (thanks zbyl2!), here is another way. This compiles fine, I think the logic works, and except for the original problem this should, with minor tweaking, work for you.

What I would like to ask the more knowledgable - how would I subroutine-ize the repeated portion of the code, so that I could just have the main script call it twice rather than having the text there for both instances of use? I know I would need to consolidate variables by making oParty (or oParty2, being arbitrary) be assigned to the regular (non-custom) party member. I had tried to do just that, but I am doing something wrong when I try to make subroutines... I think it has to do with where I am declaring my variables :compcry:

Anyway, here ya go!
Code:

void main()
{
  object oPC=GetFirstPC();
  object oCustom=GetObjectByTag("p_avix");
  object oParty=GetNextPC();
  object oParty2=GetNextPC();
  object oDoor=GetObjectByTag("man26ad_door02");

  if ((oParty != oCustom) && (oParty2 != oCustom))
  {
      SendMessageToPC(oPC,"Something's screwy around here");
  }
  else
  {
      if (oParty != oCustom)
      {
        ActionPauseConversation();
        SetCommandable(TRUE,oDoor);
        SetCommandable(TRUE,oCustom);
        SetCommandable(TRUE,oParty);
        SetCommandable(TRUE,oPC);
        AssignCommand(oDoor, ActionOpenDoor(oDoor));
        ActionWait(1.0);
        AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
        AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
        AssignCommand(oParty, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
        DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
        ActionResumeConversation(); 
      }
      else
      {
        ActionPauseConversation();
        SetCommandable(TRUE,oDoor);
        SetCommandable(TRUE,oCustom);
        SetCommandable(TRUE,oParty2);
        SetCommandable(TRUE,oPC);
        AssignCommand(oDoor, ActionOpenDoor(oDoor));
        ActionWait(1.0);
        AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
        AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
        AssignCommand(oParty2, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
        DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
        ActionResumeConversation(); 

      }
  }
}


zbyl2 09-26-2011 10:31 AM

Quote:

Originally Posted by Qui-Gon Glenn (Post 2791878)
What I would like to ask the more knowledgable - how would I subroutine-ize the repeated portion of the code, so that I could just have the main script call it twice rather than having the text there for both instances of use? I know I would need to consolidate variables by making oParty (or oParty2, being arbitrary) be assigned to the regular (non-custom) party member. I had tried to do just that, but I am doing something wrong when I try to make subroutines... I think it has to do with where I am declaring my variables :compcry:

Not sure if it's exactly what you wanted, but it doesn't have same code repeated twice anyway. Probably not the best way to do it, but it should work. I think.
Code:

void main()
{
  object oPC=GetFirstPC();
  object oCustom=GetObjectByTag("p_avix");
  object oParty=GetNextPC();
  object oParty2=GetNextPC();
  object oDoor=GetObjectByTag("man26ad_door02");

  // Define oProper object, which we'll use instead of oParty/oParty2 later.
  object oProper;

  if(oParty != oCustom && oParty2 != oCustom)
  {
      SendMessageToPC(oPC,"Something's screwy around here");
      return;
  }
 
  //figure out who's who
  if(oParty != oCustom) {
        oProper = oParty;
    } else {
    oProper = oParty2;
    }
 
      // your code, with oParty replaced by oProper.
        ActionPauseConversation();
        SetCommandable(TRUE,oDoor);
        SetCommandable(TRUE,oCustom);
        SetCommandable(TRUE,oProper);
        SetCommandable(TRUE,oPC);
        AssignCommand(oDoor, ActionOpenDoor(oDoor));
        ActionWait(1.0);
        AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
        AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
        AssignCommand(oProper, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
        DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
        ActionResumeConversation(); 

}

Nice thread by the way QGG - it saved me from boredom of setting up camera angles once more... need to get back to it now, though.

Fallen Guardian 09-26-2011 07:08 PM

Alright, I tried your script Zybl and still no luck. I have a script similar to this, except it only involves one person and it's another NPC. It has the NPC walk through a door that is opened a few seconds before he walks and it works fine, what do you think is going wrong with the script you posted?

EDIT: I seem to have many scripting troubles, anyway I have this script here:

Code:

void main()
{

  object oJerico = GetObjectByTag("n_jercor");

  object oSpeeder = GetObjectByTag("jerico_speeder");


        SetGlobalFadeOut(0.5, 1.5);
             
object oSound=GetObjectByTag("swoopidleloopj");
SoundObjectStop(oSound);

        DelayCommand(2.4, DestroyObject(oJerico));

        DelayCommand(2.4, DestroyObject(oSpeeder));

        DelayCommand(3.0,SetGlobalFadeIn(0.5, 1.5));

}

I've been puzzling over it for about twenty minutes for one reason, and one reason alone: It won't fade in. Everything else executes except the fade in. Any ideas?

TimBob12 09-27-2011 11:09 AM

What a cool idea, this is one to watch.

Qui-Gon Glenn 09-27-2011 03:58 PM

Code:

void main()
{

  object oJerico = GetObjectByTag("n_jercor");
  object oSpeeder = GetObjectByTag("jerico_speeder");
  object oSound=GetObjectByTag("swoopidleloopj");
 
  SetGlobalFadeOut(0.5, 1.5);
  SoundObjectStop(oSound);
  DelayCommand(2.4, DestroyObject(oJerico));
  DelayCommand(2.4, DestroyObject(oSpeeder));
  SetGlobalFadeIn(3.0, 1.5));

}

I think the problem there is that you were trying to double delay something. It is like adding a dimmer and dimming a lightbulb that is already on another dimmer.... it will flicker and not work properly. Just a guess, but try the new version. I simply lengthened the delay that is built into SetGlobalFadeIn to the length of your delay command.

Fallen Guardian 09-27-2011 09:32 PM

Alright it works now. Thanks.

Qui-Gon Glenn 10-03-2011 06:31 PM

:yoda: Pass on what you have learned...

In this thread, we have discussed Fallen Guardian's walking-through-doorway-dialog. We have worked out some things, although the door behavior to my understanding is less than perfect. I need to play-test and tweak it myself perhaps to see if I can suss it out... Anywho, troubleshooting is not the discussion of this post :D

I have been wanting to use "subroutines" in my code writing, but was not 100% sure how to implement them, having failed numerous times with close guesses and not-so-close ones, at how to accomplish this task properly. It mostly comes down to how you can pass variables between the "main" function and the "sub" function. What TimBob was able to show me was that what had been my goal was not wholly satisfied, yet potentially very useful.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

For starters, I am going to borrow a portion of an excellent starter tutorial done by tk102 (one of many BTW), in this thread. Especially this part, and I quote tk102 without tags here solely for easier reading:

Function declaration
Parenthesis present, equals sign absent
Code:

object CreateItemOnObject(string sItemTemplate, object oTarget, int nStackSize);
^      ^                  ^                    ^              ^
|      |                  |                    |              |- an integer parameter 
|      |                  |                    |- an object parameter
|      |                  |-a string parameter
|      |-the function
|-data type of what the function returns (an object) or void if it is an action script

I place this here so that those less familiar with this discussion can bone up on the basic function of the function :p One thing tk didn't mention here is that you can also make your function "void". Void means that it returns no data at all - it simply does ACTION in the script. Usually, when we write code, we use just one actual function, and so if it is a void function, we use
Code:

void main()
However, in the case I was investigating, I needed a subroutine, also void, separate from the main. Why? The logic works simpler, less substitution of variables, etc... you'll see! This is a bit repetitious, but the following is the code we had quasi-working earlier in this thread. Quasi in that the door was still not 100% compliant.
Code:

void main()
{
  object oPC=GetFirstPC();
  object oCustom=GetObjectByTag("p_avix");
  object oParty=GetNextPC();
  object oParty2=GetNextPC();
  object oDoor=GetObjectByTag("man26ad_door02");

  if ((oParty != oCustom) && (oParty2 != oCustom))
  {
      SendMessageToPC(oPC,"Something's screwy around here");
  }
  else
  {
      if (oParty != oCustom)
      {
        ActionPauseConversation();
        SetCommandable(TRUE,oDoor);
        SetCommandable(TRUE,oCustom);
        SetCommandable(TRUE,oParty);
        SetCommandable(TRUE,oPC);
        AssignCommand(oDoor, ActionOpenDoor(oDoor));
        ActionWait(1.0);
        AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
        AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
        AssignCommand(oParty, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
        DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
        ActionResumeConversation(); 
      }
      else
      {
        ActionPauseConversation();
        SetCommandable(TRUE,oDoor);
        SetCommandable(TRUE,oCustom);
        SetCommandable(TRUE,oParty2);
        SetCommandable(TRUE,oPC);
        AssignCommand(oDoor, ActionOpenDoor(oDoor));
        ActionWait(1.0);
        AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
        AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
        AssignCommand(oParty2, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
        DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
        ActionResumeConversation(); 

      }
  }
}

If you look at the code, you will notice that most of the action and :words: are in the conditional statement of the script. Sandwiching the inside else are two segments of almost identical code. I felt entitled to type less and just have to do it once. What I tried to make happen was not working, as I was, amongst other things, not declaring variables correctly in the naming of the function. TimBob12 was kind enough to work with me on this matter, and with my interjections in orange, this is what he showed me:
Quote:

Originally Posted by TimBob12
Variables cannot be shared between functions and main unless declared between the parenthesis of the function name. For this to work it must be declared in the initial function. This also allows custom variables to be used just by putting them straight into the function rather than declaring them (if that makes sense)It does make sense, now!

Some variables can be placed straight in the function such as GetFirstPC(); as the parameters for these never change..

These then get set with custom values. Indeed, pay attention to the use of iCustom vs oCustom in the code... I think of it as an inCustom and an outCustom, just shortened to i/o.

Here is the completed script that compiles with no errors and should work fine I have tweaked it a bit for further simplicity by passing a third variable directly into the sub, eliminating the need for some of the variables TB12 had declared in the subroutine, and eliminating a variable substitution in the main

Code:

void doorwalker(object iCustom, object iParty, object iDoor);

void doorwalker(object iCustom, object iParty, object iDoor)
{
  object oPC = GetFirstPC();
  object oCustom = iCustom;
  object oDoor = iDoor;
  object oParty = iParty;

  ActionPauseConversation();
  SetCommandable(TRUE,oDoor);
  SetCommandable(TRUE,oCustom);
  SetCommandable(TRUE,oParty);
  SetCommandable(TRUE,oPC);
  AssignCommand(oDoor, ActionOpenDoor(oDoor));
  ActionWait(1.0);
  AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
  AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
  AssignCommand(oParty, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
  DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
  ActionResumeConversation(); 
}

void main()
{
  object oPC = GetFirstPC();
  object oCustom = GetObjectByTag("customnpctag");
  object oParty = GetNextPC();
  object oParty2 = GetNextPC();
  object oDoor = GetObjectByTag("man26ad_door02");
 
  if (oParty != oCustom && oParty2 != oCustom)
  {
      SendMessageToPC(oPC, "Something's screwy around here");
  }
  else
  {
      if (oParty != oCustom)
      {
        doorwalker(oCustom, oParty, oDoor);
      }
      else
      {
        doorwalker(oCustom, oParty2, oDoor);
      }
  }
}

So, as you can see, there is still a lot of code there, but perhaps the potential in a larger script for a little more elegance, if that is important to you at all. More importantly, the logic of the main code is simpler and more natural. In a larger script with repetitive behaviors, this kind of sub-routine is exactly what you might like to have lying around for many different NPC's to use as a basis for there environmental behavior.

What I somehow missed was what it is I was actually making here... it is an #include file, if I chose to make it one. In this specific a case, I think it is a one-off, and the original code to many folk is actually simpler code, and works equivalently, so this is not necessarily the preferred means. It is however essential stuff for those of you who are trying to find new or better ways to skin a cannok. I only realized this though, when I read the following:
Quote:

Originally Posted by TimBob12
You can put the doorwalker function in a different file name doorwalker.nss, put it in your overide and then #include "doorwalker".

Derp!

:lol:
Anyways, I hope that some of you found this helpful, or at least interesting. I know that I learned a bit, thanks again to TimBob12 for taking the time to thoughtfully respond to my call!!

Fallen Guardian 10-03-2011 07:46 PM

Thanks both of you.

TimBob12 10-04-2011 10:58 AM

No problem, I'm glad I could help. Perhaps I should make a tutorial on creating new functions.....

Qui-Gon Glenn 10-04-2011 01:13 PM

Quote:

Originally Posted by TimBob12 (Post 2792457)
No problem, I'm glad I could help. Perhaps I should make a tutorial on creating new functions.....

I think, with your help, we just did :D Although, it certainly could be added to.

Fallen Guardian 10-04-2011 04:00 PM

I might of said thank you prematurely. It still doesn't work, perhaps I should go about doing it another way. I'll try it a different way and get back to you.

Qui-Gon Glenn 10-04-2011 07:47 PM

^^^ It has got to be with how we are acting on the door, as the logic is sound otherwise. I will see if I can replicate it on the Endar Spire, as that is the only place I have saves for atm. Of course, I will need to do some coordinates.... arrggh.

Is the issue still with the door, or making the correct person do the action?

Fallen Guardian 10-04-2011 10:56 PM

The door won't open when the script runs. The conversation where this script occurs is triggered when you click on the door, but that shouldn't interfere with anything.

Qui-Gon Glenn 10-05-2011 02:39 AM

Ok, I think that we will be able to get that taken care of, and we will learn something new from the process. Ain't learning fun :ugh:

Hahahahahhahahaha I should be able to get to it this weekend; gotta go spend the rest of the week with The Walking Dead.

Fallen Guardian 10-05-2011 04:35 PM

Alright, thanks Glenn.

Qui-Gon Glenn 10-08-2011 05:47 PM

Hey Fallen and script curious... I think we are getting closer to your problem's solution, thanks to this thread in the Bioware NWN forums. Specifically, using AssignCommand-type commands on a door rather than on a PC/NPC seems to be the issue.

I will cook something up shortly, after checking the thread out more closely myself.

EDIT: The problem lies in this area:
Code:

  SetCommandable(TRUE,oDoor);
  SetCommandable(TRUE,oCustom);
  SetCommandable(TRUE,oParty);
  SetCommandable(TRUE,oPC);
  AssignCommand(oDoor, ActionOpenDoor(oDoor));
  ActionWait(1.0);
  AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
  AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
  AssignCommand(oParty, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
  DelayCommand(5.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));
  ActionResumeConversation();

I think it will end up being more like this:
Code:

  AssignCommand(oCustom, ActionOpenDoor(oDoor));
  ActionWait(1.0);
  AssignCommand(oCustom, ActionForceMoveToLocation (Location(Vector(423.07117, 163.83559, 8.00), 0.0)));
  AssignCommand(oPC, ActionForceMoveToLocation (Location(Vector(420.65048, 164.75064, 8.00), 0.0)));
  AssignCommand(oParty, ActionForceMoveToLocation (Location(Vector(419.32086, 165.04903, 8.00), 0.0)));
  DelayCommand(8.0, AssignCommand(oCustom, ActionCloseDoor(oDoor)));
  ActionResumeConversation();


Fallen Guardian 10-17-2011 12:20 AM

Still no luck....I shall try another solution while you continue to seek out one. Thanks for all your help btw.

Fallen Guardian 01-07-2012 02:18 AM

Okay, I've come here today to ask about another scripting woe of mine...Here seems to be something that has nothing wrong with it, yet calamity breaks loose when I attempt to use it:

Code:

void main()
{

ActionPauseConversation();


 

    object oNPC = GetObjectByTag("danm50_merc01");

    object oNPC1 = GetObjectByTag("danm50_merc02");

    object oNPC2 = GetObjectByTag("danm50_secc");

    object oNPC3 = GetObjectByTag("danm50_ethan");

    object oNPC4 = GetObjectByTag("danm50_doha");

    object oNPC5 = GetObjectByTag("danm50_dohad");

    object oNPC6 = GetObjectByTag("danm50_foyerg1");

    object oNPC7 = GetObjectByTag("danm50_foyerg2");


                object oDoor=GetObjectByTag("danm50_doorsec");


 AssignCommand(oNPC6, SetFacingPoint(GetPosition(GetObjectByTag("danm50_doorsec"))));

 AssignCommand(oNPC7, SetFacingPoint(GetPosition(GetObjectByTag("danm50_doorsec"))));
 
 AssignCommand(oNPC2, SetFacingPoint(GetPosition(GetObjectByTag("danm50_doorsec"))));

DelayCommand(1.0, AssignCommand(oNPC6, ActionForceMoveToLocation (Location(Vector(-42.73, 121.91, 57.50), 0.0))));

DelayCommand(1.0, AssignCommand(oNPC7, ActionForceMoveToLocation (Location(Vector(-47.20, 121.91, 57.50), 0.0))));

DelayCommand(1.0, AssignCommand(oNPC4, ActionForceMoveToLocation (Location(Vector(-46.96,123.91,57.50),0.0))));

DelayCommand(1.0, AssignCommand(oNPC5, ActionForceMoveToLocation (Location(Vector(-45.12,124.03,57.50),0.0))));

DelayCommand(1.0, AssignCommand(oNPC3, ActionForceMoveToLocation (Location(Vector(-42.95,124.00,57.50),0.0))));

DelayCommand(1.0, AssignCommand(oNPC, ActionForceMoveToLocation (Location(Vector(-42.61,126.81,57.50),0.0))));

DelayCommand(1.0, AssignCommand(oNPC1, ActionForceMoveToLocation (Location(Vector(-46.89,126.65,57.50),0.0))));

DelayCommand(1.0, AssignCommand(oNPC2, ActionForceMoveToLocation (Location(Vector(-44.99,122.16,57.50),0.0))));

DelayCommand(4.0, AssignCommand(oDoor, ActionOpenDoor(oDoor)));

DelayCommand(8.0, AssignCommand(oDoor, ActionCloseDoor(oDoor)));

ActionResumeConversation();
}

Now I know it's a behmoth filled with a bunch of walking commands, but I used a script with the exact same number of walking commands earlier in the same module (Check Dantooine Tension's last video update for visual). Basically, what occurs when I use this script is a half-way achievement of what it should do.

danm50_foyerg's 1 and 2, danm50_secc, and danm50_doha all move to just infront of the door, rather than passing through it and continuing to their specified location. The other NPC's take 1-3 hesitant steps and then stop completely. I have no idea what's going on here, perhaps someone could shed some light?

JCarter426 01-07-2012 04:19 AM

Quote:

Originally Posted by Fallen Guardian (Post 2800792)
danm50_foyerg's 1 and 2, danm50_secc, and danm50_doha all move to just infront of the door, rather than passing through it and continuing to their specified location. The other NPC's take 1-3 hesitant steps and then stop completely. I have no idea what's going on here, perhaps someone could shed some light?

Hard to tell without seeing it, but ActionForceMoveTo should only fail if the destination does not exist - unlike the regular ActionMoveTo, which can fail for other reasons. So my guess would be your use of vectors rather than waypoints is responsible. I can think of a number of reasons why vectors wouldn't work when there are doors involved. Waypoints, however, are usually a safe bet because they're spawned right when the module is loaded. Since you're working with your own module anyway, you don't have to worry about compatibility.

Or it could be the door isn't fully open when they get to it. Try having it open after they start, but a lot sooner than you have it now, and see if that works - before you undertake the tedious process of editing the module. I also have a script lying around that opens a door when a creature gets close enough to it, which would rule out the need for a DelayCommand:
Code:

        object oTarget = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, 1);

if( GetIsObjectValid(oTarget) && GetDistanceBetween(OBJECT_SELF, oTarget) <= 10.0 && !GetIsOpen(oTarget) ){
        AssignCommand(oTarget, ActionUnlockObject(oTarget));
        AssignCommand(oTarget, ActionOpenDoor(oTarget));
        }

I use it as a subroutine and have the creature fire it every tenth of a second for as long as I need them to. Of course, you could easily narrow down the time, since you have a general idea of when the door needs to open and when it shouldn't anymore. But a set distance should help resolve any incongruities between the length of the door animation and the actual length it takes for the door object to open.
Quote:

Originally Posted by Qui-Gon Glenn (Post 2792775)
Hey Fallen and script curious... I think we are getting closer to your problem's solution, thanks to this thread in the Bioware NWN forums. Specifically, using AssignCommand-type commands on a door rather than on a PC/NPC seems to be the issue.

There's no reason AssignCommand shouldn't work on a door. It's used all the time in the games' scripts. I usually prefer to use it on the door, because that opens it immediately; if you use it on a creature, it will walk or run up to the door and try to open it, exactly what happens when you click on a door in the game, which is a bit rubbish when you're dealing with cutscenes. And if you're closing the door - don't even get me started on if you're closing it.

SetCommandable, on the other hand, is only used to allow/disallow a creature to add things to their action menu - to attack another creature, to use or change items, to open doors, etc. Moving is just about the only thing one can do when this is set to false. And it wouldn't do anything in this situation, because the only commands are movements. Frankly you shouldn't use this function in any situation, ever, because it should always be set to true.

Yes, I know that was in October. No, I don't care. :xp:

Fallen Guardian 01-07-2012 12:35 PM

Thanks JC, I fixed the problem using waypoints.

Qui-Gon Glenn 01-07-2012 09:51 PM

Glad that you are still working things out, Fallen Guardian :D

Thanks for your insight on the use of AssignCommand RE: doors.
I also thought that SetCommandable was not being used properly there, but never bothered to dig deeper.

I don't care that it was in October either, JCarter426 :D

JCarter426 01-07-2012 11:07 PM

Yeah, I've only seen SetCommandable used in certain combat scripts, and NPC exit scripts (so you can't talk to them as they're leaving).

EDIT: I managed to resolve my problem, but I guess some of you might be interested in the script anyway, so here you go:
Code:

void main() {

string sParam = GetScriptStringParameter();
int iFaction = GetScriptParameter(1);
int iAll = GetScriptParameter(2);
int iP3 = GetScriptParameter(3);

int iNum;
        if( iP3 == 0 ) iNum = 1;
        else iNum = iP3;

object oCreature;
        string sTag;

        if( sParam == "OBJECT_NEAREST" ){
                oCreature = GetNearestObject(1, OBJECT_SELF, iNum);
                sTag = GetTag(oCreature);
                }
        else {
                sTag = sParam;
                oCreature = GetNearestObjectByTag(sTag, OBJECT_SELF iNum);
                }

ChangeToStandardFaction(oCreature, iFaction);
DelayCommand(1.0, AssignCommand(oCreature, ActionAttack(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oCreature, 1, -1, -1, -1, -1 ), 0)));

if( iAll > 0 ){

        int i = 1;

        for(;;) {
                i++;
                object oCreature1 = GetNearestObjectByTag(sTag, OBJECT_SELF, i);
                if( GetIsObjectValid(oCreature1) ){
                        ChangeToStandardFaction(oCreature1, iFaction);
                        DelayCommand(1.0, AssignCommand(oCreature1, ActionAttack(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oCreature1, 1, -1, -1, -1, -1 ), 0)));
                        }
                else break;
                }

        }

}

This changes an NPC's faction - or, depending on how the parameters are set up, all NPCs with the same tag. And in case you're wondering, the ActionAttack is in there because some factions don't seem to immediately attack their enemies on sight.

My problem was that one NPC seemed to be excluded 95% of the time, which I resolved by using GetNearestObjectByTag rather than GetObjectByTag, which is screwy... clearly why GetNearestObjectByTag exists. However, GetObjectByTag does recognize 0, and inputting 0 doesn't immediately crash the game. So there's that.

UltimateHK47 01-08-2012 05:49 PM

I know this is a little of topic but

void main() {
object oPC=GetFirstPC();
AddMutliClass(CLASS_TYPE_JEDISENTINEL,oPC);
GiveXPToCreature(oPC,1500);
CreateItemOnObject("g_a_jedirobe01",oPC);
CreateItemOnObject("g_w_shortsbr01",GetFirstPC());
}

I want to make my character into a Jedi Sentinel but the AddMultiClass wont compile. Can someone point me in the right direction please. Thanks.

JCarter426 01-08-2012 06:39 PM

First, you don't need to add any XP. Adding a class automatically gives you enough to level up. Second, you've misspelled "multiclass" as "mutliclass". Don't feel bad, I did the same thing in my dialogue file when I was working on this. :p Third, CreateItemOnObject won't make you equip it, if that's what you want - although you couldn't equip it anyway until you actually level up, because you don't have the right feats.

Qui-Gon Glenn 01-11-2012 10:15 PM

Quote:

Originally Posted by JCarter426 (Post 2800864)
My problem was that one NPC seemed to be excluded 95% of the time, which I resolved by using GetNearestObjectByTag rather than GetObjectByTag, which is screwy... clearly why GetNearestObjectByTag exists. However, GetObjectByTag does recognize 0, and inputting 0 doesn't immediately crash the game. So there's that.

Interesting... I have wondered what the specific purpose for these two different but similar functions were. Good to know that GetNearestObjectByTag can be more reliable :thumbsup:

JCarter426 01-12-2012 02:03 AM

Yeah, while GetObjectByTag is supposed to be able to get the next nearest object and so on, it doesn't really seem to work. Sometimes the nearest creature to me would be excluded, which makes no sense because it should be the very first to be turned... and sometimes the creature furthest from me would be excluded, and sometimes someone in between. I suspect it might have more to do with the object's current location within the module. In any case, GetNearestObjectByTag works just like you'd think it does. Since this worked out perfectly, I've gone back and edited some of my older scripts to incorporate a few cool things, like destroying all objects of a certain tag - without the need to input the tag, if you don't know it.

I do, however, have another problem. Not so much as a problem as a frustration, actually. I don't think there are script functions that can achieve what I really want. I'd like to duplicate a party member but somehow change their dialogue file so I give them new dialogue without having to edit the original, but I can't think of any way to do this without editing the OnDialogue script, which of course I also don't want to do. Failing that, I considered making my own UTCs and copying the party member stats... but there's no way to do that apart from going over every single possibility one at a time, and even then there doesn't seem to be a way to duplicate some information, such as character level. One last thing I have considered is a usable item that will start a dialogue with the nearest creature, ignoring its natural dialogue entirely. But I'm not sure even that will get me what I want, because I think I might have to give them a new heartbeat script too.

So, any ideas would be most appreciated - or just thoughts on which of these would be the most ideal solution.


All times are GMT -4. The time now is 12:07 PM.

Powered by vBulletin®
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
LFNetwork, LLC ©2002-2011 - All rights reserved.