Note: LucasForums Archive Project
The content here was reconstructed by scraping the Wayback Machine in an effort to restore some of what was lost when LF went down. The LucasForums Archive Project claims no ownership over the content or assets that were archived on archive.org.

This project is meant for research purposes only.

Spawning NPC in a relative location

Page: 1 of 1
 Princess Artemis
08-06-2006, 7:22 PM
#1
I'm asking mainly if this is possible...I have an idea for a utility that perhaps some people might find useful.

Anyway, I'd need first to know if there would be a way to create an item, such as an unlimited use armband, that could, when activated, start a dialogue that could then fire a script that would spawn a .utc anywhere. For example, spawn a duplcate Mission or Atton a few feet in front of the user, no matter where they were.
 stoffe
08-06-2006, 8:43 PM
#2
I'm asking mainly if this is possible...I have an idea for a utility that perhaps some people might find useful.

Anyway, I'd need first to know if there would be a way to create an item, such as an unlimited use armband, that could, when activated, start a dialogue that could then fire a script that would spawn a .utc anywhere. For example, spawn a duplcate Mission or Atton a few feet in front of the user, no matter where they were.

Should be possible. Add a new spells.2da entry that triggers when you activate the armband (via a Activate property). The impact script of the spell would just initiate conversation with the player when activated, like:

void main() {
ActionStartConversation(GetFirstPC(), "dlgResRef");
}

(Replace the part in orange with the ResRef of your new dialog file.)

Then your conversation would trigger a script that spawns an NPC 1.5 meters in front of the NPC running the script at the appropriate time, like:

void main() {
float fFace = GetFacing(OBJECT_SELF);
location lLoc = Location(GetPosition(OBJECT_SELF) + AngleToVector(fFace) * 1.5, fFace-180.0);
CreateObject(OBJECT_TYPE_CREATURE, "SpawnedNPC", lLoc);
}

(Replace the part in blue with the distance, in meters, you want the creature to spawn in front of the script running object (i.e. the player), and the part in yellow with the ResRef of the UTC to spawn the NPC from.)
 Princess Artemis
08-06-2006, 9:17 PM
#3
Sweet. I'll try it out (these should work for both KotORs?), and if I can get it doing what I want...maybe I'll manage to make something useful out of it :)
 Darth333
08-08-2006, 2:34 PM
#4
Something like this? http://www.lucasforums.com/showthread.php?t=135519)

Feel free to use what you want from the mod.
 Princess Artemis
08-08-2006, 2:59 PM
#5
::looks, nabs::

Could be :) I'll test it and see what I can do with it...it might be pretty close to what I want. Thank you so much for letting me use it!

What I'm thinking of doing is making something that will spawn a person, droid, a small variety of characters, and then you can tell it to perform all the animations from the game, and the spawned creature would say, "I'm performing (insert animation here)" or something similar. Hopefully without writing a hundred thousand scripts. I just think it would be helpful to know what an animation looks like before writing it all up only to find out it's really goofy or doesn't work or is just not what is wanted. Or to help find out if a creature doesn't have the animation at all right off.

I'm guessing this would be a big project, which is why I wanted to know it was possible before even trying it :)
 stoffe
08-08-2006, 4:08 PM
#6
What I'm thinking of doing is making something that will spawn a person, droid, a small variety of characters, and then you can tell it to perform all the animations from the game, and the spawned creature would say, "I'm performing (insert animation here)" or something similar. Hopefully without writing a hundred thousand scripts.


If you just want to loop through all the animations listed in the DLG Editor with a few seconds delay to play each you could do that in a single script that you fire from your character's dialog. It might for example look something like:


int GetAnimationNumber(int iCnt);
void PlayNextAnimation(float fDelay, int iCnt, int iMax);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void main() {
// Delay between each animation (and length of looping animations)
float fDelay = IntToFloat(GetScriptParameter(1));

// What animation count (not number!) to start at, range is from 0 to 177
int iStartAt = GetScriptParameter(2);

// What animation count (not number!) to stop at, range is from 0 to 177
int iStopAt = GetScriptParameter(3);

if (iStartAt <= iStopAt)
PlayNextAnimation(fDelay, iStartAt, iStopAt);
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PlayNextAnimation(float fDelay, int iCnt, int iMax) {
if (iCnt <= iMax) {
int iAnim = GetAnimationNumber(iCnt);
if (iAnim != -1) {
SetCustomToken(1230, "Attempting to perform animation number " + IntToString(iAnim));
BarkString(OBJECT_INVALID, 136380);
PlayAnimation(iAnim, 1.0, fDelay);
DelayCommand(fDelay, PlayNextAnimation(fDelay, ++iCnt, iMax));
}
}
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int GetAnimationNumber(int iCnt) {
switch (iCnt) {
case 0: return 1006; break;
case 1: return 1011; break;
case 2: return 1013; break;
case 3: return 1014; break;
case 4: return 1016; break;
case 5: return 1017; break;
case 6: return 1019; break;
case 7: return 1020; break;
case 8: return 1022; break;
case 9: return 1024; break;
case 10: return 1026; break;
case 11: return 1027; break;
case 12: return 1028; break;
case 13: return 1029; break;
case 14: return 1030; break;
case 15: return 1033; break;
case 16: return 1034; break;
case 17: return 1035; break;
case 18: return 1038; break;
case 19: return 1039; break;
case 20: return 1040; break;
case 21: return 1041; break;
case 22: return 1042; break;
case 23: return 1044; break;
case 24: return 1055; break;
case 25: return 1058; break;
case 26: return 1070; break;
case 27: return 1120; break;
case 28: return 1121; break;
case 29: return 1124; break;
case 30: return 1125; break;
case 31: return 1126; break;
case 32: return 1127; break;
case 33: return 1137; break;
case 34: return 1139; break;
case 35: return 1148; break;
case 36: return 1149; break;
case 37: return 1150; break;
case 38: return 1154; break;
case 39: return 1155; break;
case 40: return 1163; break;
case 41: return 1164; break;
case 42: return 1200; break;
case 43: return 1201; break;
case 44: return 1202; break;
case 45: return 1203; break;
case 46: return 1214; break;
case 47: return 1219; break;
case 48: return 1220; break;
case 49: return 1221; break;
case 50: return 1222; break;
case 51: return 1223; break;
case 52: return 1224; break;
case 53: return 1225; break;
case 54: return 1226; break;
case 55: return 1403; break;
case 56: return 1404; break;
case 57: return 1405; break;
case 58: return 1406; break;
case 59: return 1407; break;
case 60: return 1408; break;
case 61: return 1409; break;
case 62: return 1410; break;
case 63: return 1411; break;
case 64: return 1412; break;
case 65: return 1413; break;
case 66: return 1415; break;
case 67: return 1418; break;
case 68: return 1421; break;
case 69: return 1424; break;
case 70: return 1425; break;
case 71: return 1426; break;
case 72: return 1427; break;
case 73: return 1428; break;
case 74: return 1431; break;
case 75: return 1499; break;
case 76: return 1500; break;
case 77: return 1501; break;
case 78: return 1503; break;
case 79: return 1504; break;
case 80: return 1505; break;
case 81: return 1506; break;
case 82: return 1507; break;
case 83: return 1508; break;
case 84: return 1510; break;
case 85: return 1511; break;
case 86: return 1600; break;
case 87: return 1601; break;
case 88: return 1604; break;
case 89: return 10006; break;
case 90: return 10011; break;
case 91: return 10013; break;
case 92: return 10014; break;
case 93: return 10016; break;
case 94: return 10017; break;
case 95: return 10019; break;
case 96: return 10020; break;
case 97: return 10022; break;
case 98: return 10024; break;
case 99: return 10026; break;
case 100: return 10027; break;
case 101: return 10028; break;
case 102: return 10029; break;
case 103: return 10030; break;
case 104: return 10033; break;
case 105: return 10034; break;
case 106: return 10035; break;
case 107: return 10038; break;
case 108: return 10039; break;
case 109: return 10040; break;
case 110: return 10041; break;
case 111: return 10042; break;
case 112: return 10044; break;
case 113: return 10055; break;
case 114: return 10058; break;
case 115: return 10070; break;
case 116: return 10120; break;
case 117: return 10121; break;
case 118: return 10124; break;
case 119: return 10125; break;
case 120: return 10126; break;
case 121: return 10127; break;
case 122: return 10137; break;
case 123: return 10139; break;
case 124: return 10148; break;
case 125: return 10149; break;
case 126: return 10150; break;
case 127: return 10154; break;
case 128: return 10155; break;
case 129: return 10163; break;
case 130: return 10164; break;
case 131: return 10200; break;
case 132: return 10201; break;
case 133: return 10202; break;
case 134: return 10203; break;
case 135: return 10214; break;
case 136: return 10219; break;
case 137: return 10220; break;
case 138: return 10221; break;
case 139: return 10222; break;
case 140: return 10223; break;
case 141: return 10224; break;
case 142: return 10225; break;
case 143: return 10226; break;
case 144: return 10403; break;
case 145: return 10404; break;
case 146: return 10405; break;
case 147: return 10406; break;
case 148: return 10407; break;
case 149: return 10408; break;
case 150: return 10409; break;
case 151: return 10410; break;
case 152: return 10411; break;
case 153: return 10412; break;
case 154: return 10413; break;
case 155: return 10415; break;
case 156: return 10418; break;
case 157: return 10421; break;
case 158: return 10424; break;
case 159: return 10425; break;
case 160: return 10426; break;
case 161: return 10427; break;
case 162: return 10428; break;
case 163: return 10431; break;
case 164: return 10499; break;
case 165: return 10500; break;
case 166: return 10501; break;
case 167: return 10503; break;
case 168: return 10504; break;
case 169: return 10505; break;
case 170: return 10506; break;
case 171: return 10507; break;
case 172: return 10508; break;
case 173: return 10510; break;
case 174: return 10511; break;
case 175: return 10600; break;
case 176: return 10601; break;
case 177: return 10604; break;
default : return -1; break;
}
return -1;
}


For this example to work you'd need to add a single new entry to dialog.tlk which only contains the text:

<CUSTOM1230>

Then replace the part in yellow in the above script with the StrRef of your added TLK entry.

This is adapted for KotOR2 where you set the following script parameters on the dialog node where you use the script:
The first parameter (P1) is the delay (in seconds) between each animation (or the duration of the animation in the case of looping anims)
The second parameter (P2) is the animation count to start looping at, the range is 0-177.
The third parameter (P3) is the animation count to stop looping at, the range is 0-177.


The last two are for if you want to split up the playing of the animations in more digestible chunks and not attempt to play all 178 of them in one go. :)

This should (unless I've made some mistake) play all animations in the specified interval, with the set delay between each to allow them to play. At each new animation the NPC will float a message saying which animation it attempts to play.

Since not all creatures have all animations you should be able to see which won't work since the NPC would just be standing there while they say that they play an animation.
 Princess Artemis
08-08-2006, 6:05 PM
#7
So that script would work in K2 only? Sorry, I'm getting confused <: ) I think I understand how it works, though, sorta, kinda, a little. To adapt it to K1, I'd have to find the numbers and set the delay, etc. in the script?

Anyway, I tried it in K2...and the newly fixed version works beautifully :D If I wanted the animation character to 'say' something different, like, "Attempting to perform animation number 10016 DROP_TO_KNEES", I could add that after the return in each case?
 stoffe
08-08-2006, 6:41 PM
#8
So that script would work in K2 only? Sorry, I'm getting confused <: ) I think I understand how it works, though, sorta, kinda, a little. To adapt it to K1, I'd have to find the numbers and set the delay, etc. in the script?


Yes, the example script was for KotOR2, since you can't pass any parameters to a script in KotOR1 (you can hardcode the delay and start/stop values though), and the animations are not necessarily the same. KotOR2 certainly has more animations than KotOR1 has, and I am unaware if the ones both have are at the same animation numbers. So you'd have to replace the "table" with animation numbers with one from KotOR1. :)


Anyway, I tried it in K2...and the newly fixed version works beautifully :D If I wanted the animation character to 'say' something different, like, "Attempting to perform animation number 10016 DROP_TO_KNEES", I could add that after the return in each case?

To add an "explanation text" for each animation number you could make a separate function that work similarily to the GetAnimationNumber() function, but which returns a text string instead of an integer value. If you keep the "count" parameter the same you could get both the animation number and description for each iteration, like (text in yellow is new additions):


(...snip...)
void PlayNextAnimation(float fDelay, int iCnt, int iMax) {
if (iCnt <= iMax) {
int iAnim = GetAnimationNumber(iCnt);
if (iAnim != -1) {
SetCustomToken(1230, "Performing animation " + IntToString(iAnim) + "(" + GetAnimationName(iCnt) + ")");
BarkString(OBJECT_INVALID, 136380);
PlayAnimation(iAnim, 1.0, fDelay);
DelayCommand(fDelay, PlayNextAnimation(fDelay, iCnt++, iMax));
}
}
}


string GetAnimationName(int iCnt) {
switch (iCnt) {
case 0: return "DEAD"; break;
case 1: return "DROP_TO_KNEES"; break;
(...snip - continue list for all anim entries...)
case 177: return "UNKNOWN"; break;
defaullt: ""; break;
}
return "";
}

(...snip...)
 Princess Artemis
08-08-2006, 9:18 PM
#9
Ah, coolness. I think I'll be able to bake something up with this for both games. Thank you so much!

Edit: I had to add string GetAnimationName(int iCnt); to the top of the script, but it worked fine after that.

And, ::happy feet:: actually, the script is playing the non-dialogue animations instead :D I was wondering how I'd do that, but the solution was right there! I can use DLGEditor to play dialogue animations.

It's gonna take me forever to write them all down, descriptions and all, in a script and get it all sorted out so that I can have the player spawn an appropriate character for different things, but at least for K2, with your script stoffe and your armband Darth333, I think I might be able to get something made that maybe someone will find useful. I know the script will play battle animations, too, and that's exactly what I wanted.

Your awesomeness and helpfulness is a sight to behold :D
Page: 1 of 1