PDA

View Full Version : Creating Custom Functions


Qui-Gon Glenn
10-04-2011, 01:17 PM
Originally posted by Me with a lot of help from TB12!

: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 (http://www.lucasforums.com/showthread.php?t=143390). Especially this part, and I quote tk102 without tags here solely for easier reading:

Function declaration
Parenthesis present, equals sign absent
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 scriptI 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 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.
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:

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:
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!!