Jump to content

Home

Qui-Gon's Script Shack


Qui-Gon Glenn

Recommended Posts

^^^ 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?

Link to comment
Share on other sites

  • Replies 352
  • Created
  • Last Reply

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:

   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:

   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();

Link to comment
Share on other sites

  • 2 weeks later...
  • 2 months later...

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:

 

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?

Link to comment
Share on other sites

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:

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.

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:

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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:

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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:
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

 

Thanks, i picked the script somewhere else but that clears it up. Second, the Createitem bit, I wanted the items to go to my inven. Not equip. Like i level up and then get a lightsaber. Would that work?

Thanks for all yorur help.

Link to comment
Share on other sites

Well, recruiting an NPC is pretty simple. Assuming you have your basic talk to NPC and then recruit them, it should go something like this:

void main() {

object oNPC = GetObjectByTag("NPC_tag", 0);
int iNPC = NPC_INTEGER;

AddAvailableNPCByObject(iNPC, oNPC);
SetNPCSelectability(iNPC, 1);

}

 

I believe you're supposed to keep the NPC tag as the tag of whoever you're replacing. So you'll have to check the original NPC for that. The NPC integers are as follows (you can paste this right into your script if you need it for reference, it won't do anything):

/*
KOTOR
int NPC_BASTILA         = 0;
int NPC_CANDEROUS       = 1;
int NPC_CARTH           = 2;
int NPC_HK_47           = 3;
int NPC_JOLEE           = 4;
int NPC_JUHANI          = 5;
int NPC_MISSION         = 6;
int NPC_T3_M4           = 7;
int NPC_ZAALBAR         = 8;
*/

/*
KOTOR 2
int NPC_ATTON           = 0;
int NPC_BAO_DUR         = 1;
int NPC_CANDEROUS       = 2;
int NPC_G0T0            = 3;
int NPC_HANDMAIDEN      = 4;
int NPC_HK_47           = 5;
int NPC_KREIA           = 6;
int NPC_MIRA            = 7;
int NPC_T3_M4           = 8;
int NPC_VISAS           = 9;
int NPC_HANHARR          = 10;
int NPC_DISCIPLE        = 11;
*/

 

You can use either the integer or the name (exactly as it appears) - depending of course on who you're replacing. You also might want to add the following, which will bring up the party selection screen and force the player to pick your new NPC:

ShowPartySelectionGUI("", iNPC, -1);

The "" is the name of a script to fire upon exiting, so put something in there (in the quotes) if you want to fire another script (such as a dialogue). Change the -1 to another NPC integer if you want another forced NPC. But if you're going to do this, you have to make sure the module is set to unescapable, or else fire the following as well:

SetAreaUnescapable(1);

...which will prevent you from changing party members, no matter what the area. Then once this is over, fire:

SetAreaUnescapable(0);

...which will let you change party members again. This function only affects a single module for as long as you're in it and is undone if you enter a new module, so if you're dealing with more than one you'll have to trigger it each time you enter a module - I believe this only ever happens in K2, and they do it by setting a global and then having all the modules' OnEnter scripts check that global.

 

So here's an example with everything, replacing Mission (because I think she's useless :xp: but feel free to change it):

void main() {

//Define everything
object oPC = GetFirstPC();
object oNPC = GetObjectByTag("mission", 0);
int iNPC = NPC_MISSION;
int iClass = CLASS_TYPE_JEDISENTINEL;

//Recruit the NPC
AddAvailableNPCByObject(iNPC, oNPC);
SetNPCSelectability(iNPC, 1);

//Multiclass to Jedi Sentinel and give items
AddMutliClass(iClass, oPC);
CreateItemOnObject("g_a_jedirobe01", oPC);
CreateItemOnObject("g_w_shortsbr01", oPC);

//Bring up the party selection GUI, force the new NPC, and prevent any further changes
SetAreaUnescapable(1);
ShowPartySelectionGUI("", iNPC, -1);

}

 

Simple, right? :D But basically, AddAvailableNPC is really the only thing required to add an NPC (no really?) to the party. It's a lot like AddMultiClass; you think because it's such an important feature it must be immensely complicated, when in fact it's one line - although the hard coded stuff that the function calls probably is immensely complicated. Anyway, it's very simple, the rest is just window dressing. But of course you should consult the tutorial here on recruiting... it goes over a lot of things I haven't covered.

Link to comment
Share on other sites

Great post JCarter426!!

 

At first reading, I was wishing there were a link to a tutorial somewhere... then realized I was reading one! Nicely done :D

 

I have a small question about this though:

void main() {

object oNPC = GetObjectByTag("NPC_tag", 0);
int iNPC = NPC_INTEGER;   [color=darkorange]<-- ???[/color]

AddAvailableNPCByObject(iNPC, oNPC);
SetNPCSelectability(iNPC, 1);

}

Where does the value NPC_INTEGER come from? It probably is an integer constant from nwscript, so I am wondering what object it is being obtained from, and how... I would think it would need to be something along the lines of

int iNPC = GetSomeInfoFromObject(oNPC); [color=darkorange]This function compiles for me all the time [/color]

I believe that what you posted is correct, so really I am asking for further enlightenment :)

Link to comment
Share on other sites

THANKS SOOO MUCh! Im sure many people will find this helpful, i get it. Your a true master JC :D

Great post JCarter426!!

 

At first reading, I was wishing there were a link to a tutorial somewhere... then realized I was reading one! Nicely done :D

No problemo and thanks. :) As I said it's not that complicated... and I've been messing around with this stuff for a while.

 

Will this still work if I have 2 seperate scripts, 1 to recruit and 1 to change the class, for instance Atton, who you add but can only have as a defferent class at a later date?

Not sure what you mean exactly... if you want to split it up into separate scripts, yes, just duplicate it and remove what isn't necessary from each part. For example:

void main() {

//Define everything
object oNPC = GetObjectByTag("mission", 0);
int iNPC = NPC_MISSION;

//Recruit the NPC
AddAvailableNPCByObject(iNPC, oNPC);
SetNPCSelectability(iNPC, 1);

//Bring up the party selection GUI, force the new NPC, and prevent any further changes
SetAreaUnescapable(1);
ShowPartySelectionGUI("", iNPC, -1);

}

 

void main() {

//Define everything
object oPC = GetFirstPC();
int iClass = CLASS_TYPE_JEDISENTINEL;

//Bring up the party selection GUI, force the new NPC, and prevent any further changes
SetAreaUnescapable(1);
ShowPartySelectionGUI("", iNPC, -1);

}

But you can't change an NPC's class... as I mentioned earlier. If you mean the multiclass to Sentinel... what you posted (and I recreated) multiclasses the player, not Atton or anyone else, so you'd have to change the object for that. I think I'm just misunderstanding you though. :p

 

Also, I forgot to mention, with any recruitment you should remove the NPC you're replacing if they're already in the party - RemoveAvailableNPC(iNPC) - and remove the possibility of recruiting them if they're not.

Where does the value NPC_INTEGER come from? It probably is an integer constant from nwscript, so I am wondering what object it is being obtained from, and how... I would think it would need to be something along the lines of
int iNPC = GetSomeInfoFromObject(oNPC); [color=darkorange]This function compiles for me all the time [/color]

I believe that what you posted is correct, so really I am asking for further enlightenment :)

Yes, lifted from nwscript.nss. Basically the number is just the slot they occupy; the names that are defined are just added for the developers' sake - I usually just use the numbers, since I've memorized them. :D There are 10 slots in K1, 13 in K2 (plus 3 puppet slots). As far as I know they're just slots, space for storing information, nothing else. (Side note: it is possible to add an NPC to a slot that isn't defined, but it's severely broken because the game doesn't store any information for it. I managed to have a non-existent NPC in my party, taking up space and preventing me from adding another, despite not existing.)

 

There's no function for the NPC integer, though - either to get an NPC's integer, or to get the object that's assigned to an integer. I guess no one ever needed it. You can, however, spawn the object that is stored in a specified slot. For my own purposes I wrote some subroutines (for K2) to rip the integer from an object, or to generate an object based on an integer (well, expected object- it's not perfect because other NPCs can and do occupy party member slots... Trask as Bastila, B-4D4 and the Remote as Bao-Dur, recruitment mods, etc):

//Gets the NPC integer of an object
int ObjectToNPC(object oObject) {

string sTag = GetTag(oObject);
if( sTag == "atton" ) return 0;
else if( sTag == "baodur" ) return 1;
else if( sTag == "mand" ) return 2;
else if( sTag == "G0T0" ) return 3;
else if( sTag == "handmaiden" ) return 4;
else if( sTag == "HK47" ) return 5;
else if( sTag == "kreia" ) return 6;
else if( sTag == "mira" ) return 7;
else if( sTag == "T3M4" ) return 8;
else if( sTag == "visasmarr" ) return 9;
else if( sTag == "hanharr" ) return 10;
else if( sTag == "disciple" ) return 11;
else return -1;

}



//Gets the expected tag of an object using its NPC integer
string NPCToTag(int iNPC) {

string sTag;
if( iNPC == 0 ) return "atton";
else if( iNPC == 1 ) return "baodur";
else if( iNPC == 2 ) return "mand";
else if( iNPC == 3 ) return "G0T0";
else if( iNPC == 4 ) return "handmaiden";
else if( iNPC == 5 ) return "HK47";
else if( iNPC == 6 ) return "kreia";
else if( iNPC == 7 ) return "mira";
else if( iNPC == 8 ) return "T3M4";
else if( iNPC == 9 ) return "visasmarr";
else if( iNPC == 10 ) return "hanharr";
else if( iNPC == 11 ) return "disciple";
else return "";

}



//Gets an object from an NPC integer
object NPCToObject(int iNPC){

return GetObjectByTag(NPCToTag(iNPC), 0);

}

I used these for a script that switches the active PC to a party member. I needed to get the integer of the current PC and store it, check if who I'm turning into is available, make sure the two aren't the same, remove who I'm switching to from the party if present so the game doesn't CRASH, add the other party members back into the party (with the integer), and various other things - and give different error messages depending on the situation. Took me days to sort out all the possibilities. :D

Link to comment
Share on other sites

Yes, that's the only thing you can do in the game, actually. AddMultiClass automatically gives you a level of the specified class, and that becomes your active class. You still have all your previous levels, all the points you spent and so on, but any new levels are in the new class. There seems to be no way to change the class of an existing object - for example, to turn Mission into a soldier or Kreia into a Sentinel. You can do it by changing the character's UTC, of course, but it's not possible within the game. Once a character has a class, you can only add a class, and only one class (trying to add another can crash the game).

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.


×
×
  • Create New...