Level Scripting Tutorial 3IntroductionWelcome to the third and last of my scripting tutorials. Well, that's probably not entirely true. Maybe I'll figure something else out and it will be worth writing about.
Anyway, In this tutorial we will take the mechanical arm from the last tutorial as well as some new pieces and create a machine. It's a pretty simple machine and it isn't perfect. But I wasn't making eye candy to begin with.
Advanced: Creating a MachineJust like in the previous tutorials I am streamlining the process. I am providing you will everything you will need.
The zip is linked to below.
FILE IS NO LONGER AVAILABLEPlanning Your ProjectThe first thing to do is to plan. You need to know what you're trying to accomplish and how to do it.
One thing that is necessary is an idea. So here is the point where I am going to trust you...
You see, I decided it would be easier to just show you it in action than draw out a bunch of diagrams. This means that everything needed including the script is included in this zip file. Of course in order to learn anything you're going to rename, move, or delete the script after you've seen what we are going to do.
So, this means your going to run the map and observe the machine running through its cycle. So...
Extract the zip to
[Your Doom 3 Directory]\base. Fire up doom, run the map
rich_scripting_tutorial_3. After it loads and starts up
you'll see these crazy robotic arms moving around and doing all kinds of weird nonsense. As you can see, I didn't do a good job of planning. Yeah, I'm not proud of it but it'll work for this tutorial.
As you can see, the robotic clasp picks up a canister and the welding gun will meet up with it and fire. Next the clasp will place the canister back and the sequence will repeat with the second canister.
Now you should have a pretty good idea what we will be trying to accomplish. But, before we start scripting away I need to give you a good idea how the parts come together to form arms and such.
*missing image*
http://www.doom3world.org/richard/grabarm.pngAnd here are all the parts of the clasp. From here on out we will just refer to this as the grab arm. You should be familiar with it already (Previous tutorial, Hint, Hint).
It is also a good idea to come up with a naming convention when we are working with several parts. This will make it easier to tell what is where. I am going to name these parts from the top down.
Here are the names for the grab arm components from top to bottom. We will also be forming a child parent tree. The items at the bottom will be parented to the items above it. Now take into consideration that the fingers will all be parented to the finger joint a.k.a.
$GrabArm_Base3.
$GrabArm_Base1
$GrabArm_Swivel1
$GrabArm_Base2
$GrabArm_Swivel2
$GrabArm_Base3
$GrabArm_Finger1
$GrabArm_Finger2
$GrabArm_Finger3*missing image*
http://www.doom3world.org/richard/sparkarm.pngIt's the same deal here. I will name the parts from top to bottom. Again the items at the bottom will be child objects to the items above.
$SparkArm_Base1
$SparkArm_Swivel1
$SparkArm_Base2
$SparkArm_Swivel2
$SparkArm_Base3
$SparkArm_Gun1We'll need a name for both canisters as well. We can name them from left to right to keep things simple. You all read from left to right don't you?
$Can1
$Can2Nutz 'n' Bolts: Screwing around in the Map EditorAfter you have planned... umm yeah, you'll fire up the editor and begin putting parts into place. It's not over yet though; you'll still need to run through the motions to figure out where everything goes and what you'll need to make it work.
Go ahead and load up
rich_scripting_tutorial_3.map in the editor. You'll see that the parts are laid out in the editor similar to the diagram below.
*missing image*
http://www.doom3world.org/richard/layout.pngOf course in the editor you'll see several entities that are not displayed here. All of these are speakers and particle generators except for three. See if you can find them.
Hint: Take a look at the base of the grab arm (where it attaches to the machine) and to the left, directly above each canister.
Guess what these are for? Well, the grab arm needs to move to each canister and then back to its original position. These entities act as markers so the arm will know where to go. Basically they are three
func_statics with the following names.
$Can_Pos1
$Can_Pos2
$Arm_Pos1It's pretty straight forward. They're named from left to right again.
We aren't interested in the sounds and special effects yet. We need to get this puppy moving before we think about what sounds or special effects to use.
Playing God: Scripting it to LifeThis would be the part where you delete, rename, move, or in other words, get rid of the script. Unless you wanna steal my work thief!

So, start up your favorite text editor editpad... What?! You haven't downloaded editpad yet?! Ah, forget it. Stick to whatever you're using. If you haven't changed yet you never will. Although I was thinking about working on a colored text filter specifically for doom 3 scripting. Oh well.
Alright. We are definitely going to need some comments in this code so we don't get all confused.
So start out with a simple script like this.
Code:
////////////////////////////////////////////////////
//
// Setup binds, etc...
//
////////////////////////////////////////////////////
void setup_objects()
{
// SparkArm Binds
// GrabArm Binds
}
////////////////////////////////////////////////////
//
// Part Movement...
//
////////////////////////////////////////////////////
////////////////////////////////////////////////////
//
// MAIN
//
////////////////////////////////////////////////////
void main ()
{
setup_objects ();
sys.wait(3);
}
You should already know what's coming next. That's right. We need to setup our binds. Let's take care of the spark arm. You should know where this goes by now.
Hint: We are setting up objects right?
Code:
// SparkArm Binds
$SparkArm_Gun1.bind($SparkArm_Base3);
$SparkArm_Base3.bind($SparkArm_Swivel2);
$SparkArm_Swivel2.bind($SparkArm_Base2);
$SparkArm_Base2.bind($SparkArm_Swivel1);
$SparkArm_Swivel1.bind($SparkArm_Base1);
And from here we setup our binds for the grab arm.
Code:
// GrabArm Binds
$GrabArm_Finger1.bind($GrabArm_Base3);
$GrabArm_Finger2.bind($GrabArm_Base3);
$GrabArm_Finger3.bind($GrabArm_Base3);
$GrabArm_Base3.bind($GrabArm_Swivel2);
$GrabArm_Swivel2.bind($GrabArm_Base2);
$GrabArm_Base2.bind($GrabArm_Swivel1);
$GrabArm_Swivel1.bind($GrabArm_Base1);
Now, we are ready to move some stuff. Let's setup a motion for the grab arm first since it's the more difficult of the two.
We aren't interested in covering the entire motion at first. In fact, we will only write enough code to grasp the first canister and move it into position. We are breaking the cycle into each component. (Remember talking about that from the first tutorial?)
First we will give this motion a name. Hmmm?.
GrabCanister sounds good. (Pretty self explanatory huh?)
So let's make a function like this.
Code:
void GrabCanister()
{
}
Now, I'll explain what needs to happen on each line and you type up the code.
Move
GrabArm_Base1 to
Can_Pos1.
Wait for
GrabArm_Base1 to finish it's movement.
Rotate
GrabArm_Finger1 once 45 degrees on the X axis.
Rotate
GrabArm_Finger2 once 45 degrees on the X axis.
Rotate
GrabArm_Finger3 once 45 degrees on the X axis.
Move
Can1 up 30.
Wait for
Can1 to finish it's movement.
Rotate
GrabArm_Finger1 once -35 degrees on the X axis.
Rotate
GrabArm_Finger2 once -35 degrees on the X axis.
Rotate
GrabArm_Finger3 once -35 degrees on the X axis.
Wait for
Finger1 to finish it's movement. All three fingers should finish at the same time.
Bind
Can1 to
GrabArm_Base3.
Rotate
GrabArm_Base3 once 360 degrees on the Y axis.
Wait for
GrabArm_Base3 to finish it's movement.
Rotate
GrabArm_Base2 once 90 degrees on the Y axis.
Wait for
GrabArm_Base2 to finish it's movement.
Rotate
GrabArm_Swivel2 once 90 degrees on the X axis.
Wait for
GrabArm_Swivel2 to finish it's movement.
Move
GrabArm_Base1 to
Arm_Pos1.
Rotate
GrabArm_Base2 once -90 on the Y axis.
Wait for
GrabArm_Base2 to finish it's movement.
Now here is the code below. Yeah you could've just cheated and copied and pasted from below. But that's really lame.
Code:
void GrabCanister()
{
$GrabArm_Base1.moveTo($Can_Pos1);
sys.waitFor($GrabArm_Base1);
$GrabArm_Finger1.rotateOnce('45 0 0');
$GrabArm_Finger2.rotateOnce('45 0 0');
$GrabArm_Finger3.rotateOnce('45 0 0');
Can1.move(UP, 30);
sys.waitFor($Can1);
$GrabArm_Finger1.rotateOnce('-35 0 0');
$GrabArm_Finger2.rotateOnce('-35 0 0');
$GrabArm_Finger3.rotateOnce('-35 0 0');
sys.waitFor($GrabArm_Finger1);
Can1.bind($GrabArm_Base3);
$GrabArm_Base3.rotateOnce('0 360 0');
sys.waitFor($GrabArm_Base3);
$GrabArm_Base2.rotateOnce('0 90 0');
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('90 0 0');
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateOnce('0 -90 0');
sys.waitFor($GrabArm_Base2);
}
Now add this function to the main function like this.
Code:
void main ()
{
setup_objects ();
sys.wait(3);
GrabCanister();
}
Save your script as
rich_scripting_tutorial_3.script and fire up the map. You've got a
mechanical arm picking up a canister.
You know what is really interesting? When it picks up the other canister it pretty much runs through the same set of motions doesn't it? Wouldn't it save you a lot of trouble if you could just call the same function to pick up the other canister? But how?
This is where we bring parameters into the equation. You see, every function has a set of parentheses appended to the end of it. You can pass objects to the functions by placing the object's names within these parentheses. All you have to do is change the function slightly.
Let's see what objects will change in the
GrabCanister function when I am picking up the second canister? Hmmm? Just two,
Can1 and
Can_Pos1. Everything else remains the same.
We will need to create alias names for the objects that change. These are called reference variables. We cannot use the names
Can1, Can2, Can_Pos1, or
Can_Pos2 in the function in order for this to work. We need to use these reference variables as aliases for the objects that are passed to the function instead. In the function we will use the names
Can and
CanPos.
When we call this modified form of the
GrabCanister function it will look like this when we pass
Can1 and
Can_Pos1.
Code:
GrabCanister($Can_Pos1, $Can1);
Or when we pass
Can2 and
Can_Pos2 it will look like this.
Code:
GrabCanister($Can_Pos2, $Can2);
And when we declare our function we will now change the line to look like this.
Code:
void GrabCanister(entity CanPos, entity Can)
{
Notice that I defined the type of variables by using the keyword entity. This is because we are passing entity names to these reference variables.
And to modify the code of the function we will replace every
Can1 with
Can and every
Can_Pos1 with
CanPos. The code should look like this.
Code:
void GrabCanister(entity CanPos, entity Can)
{
$GrabArm_Base1.moveTo(CanPos);
sys.waitFor($GrabArm_Base1);
$GrabArm_Finger1.rotateOnce('45 0 0');
$GrabArm_Finger2.rotateOnce('45 0 0');
$GrabArm_Finger3.rotateOnce('45 0 0');
Can.move(UP, 30);
sys.waitFor(Can);
$GrabArm_Finger1.rotateOnce('-35 0 0');
$GrabArm_Finger2.rotateOnce('-35 0 0');
$GrabArm_Finger3.rotateOnce('-35 0 0');
sys.waitFor($GrabArm_Finger1);
Can.bind($GrabArm_Base3);
$GrabArm_Base3.rotateOnce('0 360 0');
sys.waitFor($GrabArm_Base3);
$GrabArm_Base2.rotateOnce('0 90 0');
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('90 0 0');
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateOnce('0 -90 0');
sys.waitFor($GrabArm_Base2);
}
And now in the main function I will change the call to
GrabCanister to look like this.
Code:
void main ()
{
setup_objects ();
sys.wait(3);
GrabCanister($Can_Pos2, $Can2);
}
Save your script and fire up the map. Now we are looking at some impressive stuff. Here you are reusing the same code to move a completely different canister.So from here we can make a function to move the place the canister back. We'll call it simply
PlaceCanister.
Basically we are doing what we did above backwards. So here is the code.
Code:
void PlaceCanister(entity CanPos, entity Can)
{
$GrabArm_Base2.rotateOnce('0 90 0');
$GrabArm_Base1.moveTo(CanPos);
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('-90 0 0');
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base3.rotateOnce('0 -360 0');
sys.waitFor($GrabArm_Base3);
Can.unbind();
$GrabArm_Finger1.rotateOnce('35 0 0');
$GrabArm_Finger2.rotateOnce('35 0 0');
$GrabArm_Finger3.rotateOnce('35 0 0');
sys.waitFor($GrabArm_Finger1);
Can.rotateTo('0 0 0');
Can.move(DOWN, 30);
sys.waitFor(Can);
$GrabArm_Finger1.rotateOnce('-45 0 0');
$GrabArm_Finger2.rotateOnce('-45 0 0');
$GrabArm_Finger3.rotateOnce('-45 0 0');
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateTo('0 0 0');
sys.waitFor($GrabArm_Base1);
}
There are two lines I do want you to take note of though. Notice in this function the
unbind command. This is because I want to detach the canister from the mechanical arm rather than bind it. Also notice how I am not placing anything in the parentheses. This is because you can only bind an object to one other object at a time. So there is no need to tell the engine what to detach the canister from. There is only one possible object it could be detached from in the first place.
The second line to look at is this line...
Code:
$GrabArm_Base2.rotateTo('0 0 0');
The
rotateTo command is used here to ensure that GrabArm_Base2 is positioned the same as it was when the map first loaded. Without this command the base of the arm would start bending in the wrong direction. Go ahead and comment it out and give it a test run if you're curious.
Now we throw a call to
PlaceCanister immediately following the call to
GrabCanister.
Code:
void main ()
{
setup_objects ();
sys.wait(3);
GrabCanister($Can_Pos2, $Can2);
PlaceCanister($Can_Pos2, $Can2);
}
Save the script and start up the map. It will now grab the second canister and place it back. Go ahead and add another call to both
GrabCanister and
PlaceCanister each pointing to the first canister instead. It should look like this.
Code:
void main ()
{
setup_objects ();
sys.wait(3);
GrabCanister($Can_Pos2, $Can2);
PlaceCanister($Can_Pos2, $Can2);
GrabCanister($Can_Pos1, $Can1);
PlaceCanister($Can_Pos1, $Can1);
}
Same deal. Save and start the map. Now it will grab the second canister, place it back, and then do the same with the first canister. Neat huh?
Now we just need to code a function to move the spark gun into position. We'll call this PosPiecesToFire. Why the crazy name? Well later we will call the spark effects function from within this function. So basically this function will
position the pieces and then fire. Make sense now?
This function is pretty simple compared to the previous one. We won't be passing objects to it and its movement is simple. So here's your code.
Code:
void PosPiecesToFire ()
{
$SparkArm_Swivel2.rotateOnce('-90 0 0');
sys.waitFor($SparkArm_Swivel2);
$SparkArm_Gun1.rotateOnce('-5 0 0');
sys.waitFor($SparkArm_Gun1);
$SparkArm_Base3.rotateOnce('0 120 0');
sys.waitFor($SparkArm_Base3);
$SparkArm_Base3.rotateOnce('0 120 0');
sys.waitFor($SparkArm_Base3);
$SparkArm_Base3.rotateOnce('0 120 0');
$SparkArm_Gun1.rotateOnce('5 0 0');
$SparkArm_Swivel2.rotateOnce('90 0 0');
$SparkArm_Base3.rotateTo('0 0 0');
sys.waitFor($SparkArm_Swivel2);
}
Now change the main function to look like this.
Code:
void main ()
{
setup_objects ();
sys.wait(3);
GrabCanister($Can_Pos2, $Can2);
PosPiecesToFire();
PlaceCanister($Can_Pos2, $Can2);
GrabCanister($Can_Pos1, $Can1);
PosPiecesToFire();
PlaceCanister($Can_Pos1, $Can1);
}
Again, save the script and start up the map. Now each arm does its own thing.
Let's change something real quick. I mean we don't want this to happen once and stop right?
We're going to create a function called
MechCycle that will execute forever and a day. Basically we will cut the machine movement code from main and paste it.
Then we'll place that code in a while loop. It should look like this.
Code:
void MechCycle ()
{
while (1) {
GrabCanister($Can_Pos1, $Can1, $Can1_TubeSnd);
PosPiecesToFire();
PlaceCanister($Can_Pos1, $Can1, $Can1_TubeSnd);
GrabCanister($Can_Pos2, $Can2, $Can2_TubeSnd);
PosPiecesToFire();
PlaceCanister($Can_Pos2, $Can2, $Can2_TubeSnd);
}
}
Now, we just throw a call in
main so it looks like this.
Code:
void main ()
{
setup_objects ();
sys.wait(3);
MechCycle();
}
Save the script and run the map. Now the machine runs continuously.
Adding Bells, Whistles and a LightHere is the fun part. Everything is already moving all you got to do is make it pretty. Well, I suppose it isn't going to look too pretty unless I went through the trouble of texturing my objects in the first place. Ahhh, whatever. I'm still learning modeling anyway.
Technically this part is so simple it really isn't worth documenting but hey, I'm trying to cover it all right?
Now, I'm no expert at sound type stuff and I tried really hard to find sounds that matched the action. But hey, in all honesty you should probably be creating new sounds for your work anyway. I decided I would need eight speakers. Two upward hydraulic pump sounds, one for each arm. Two downward hydraulic pump sounds again, one for each arm. Two steam type sounds for each canister being released. A spark gun sound and finally some form of ambient mechanical hum.
The ambient sound loops and the remaining sounds are all setup to sound when triggered by setting a key called
s_waitfortrigger to 1. Here are the names of the sounds that need scripting.
$SparkArm_JointUp
$SparkArm_JointDown
$SparkArm_Snd1
$GrabArm_JointUp
$GrabArm_JointDown
$Can1_TubeSnd
$Can2_TubeSndNow because the spark arm doesn't move a significant amount the joint sounds don't need to be bound. Neither do the canister tube sounds. However, since the grab arm and the spark arm move a pretty good distance we'll bind their speakers to the moving entities.
Don't worry about the binds yet though, We'll be knocking out the sounds as well as lighting and special effects all at once.
Triggering a sound is pretty simple. Really all you have to do is perform a sys.trigger(
insert speaker name here) whenever you want that particular sound to fire. There is no need to clutter your map with actual trigger entities.
Now for the spark gun. I created a
func_fx, brought up the entities properties, set the
fx key to
fx/sparks.fx, and set the restart key to 1 so that I can trigger this effect multiple times.
Next I created a light and set the key
startoff to 1. I set the shader to
lights/roundfire.
Again, both the light and the
func_fx are triggered using the command sys.trigger(
insert light or func_fx name here).
Here is the name of the light and
func_fx...
$SparkArm_Light1
$SparkArm_Fx1So, change your binds to look like this.
Code:
void setup_objects()
{
// SparkArm Binds
$SparkArm_Fx1.bind($SparkArm_Gun1);
$SparkArm_Light1.bind($SparkArm_Gun1);
$SparkArm_Snd1.bind($SparkArm_Gun1);
$SparkArm_Gun1.bind($SparkArm_Base3);
$SparkArm_Base3.bind($SparkArm_Swivel2);
$SparkArm_Swivel2.bind($SparkArm_Base2);
$SparkArm_Base2.bind($SparkArm_Swivel1);
$SparkArm_Swivel1.bind($SparkArm_Base1);
// GrabArm Binds
$GrabArm_Finger1.bind($GrabArm_Base3);
$GrabArm_Finger2.bind($GrabArm_Base3);
$GrabArm_Finger3.bind($GrabArm_Base3);
$GrabArm_Base3.bind($GrabArm_Swivel2);
$GrabArm_Swivel2.bind($GrabArm_Base2);
$GrabArm_Base2.bind($GrabArm_Swivel1);
$GrabArm_Swivel1.bind($GrabArm_Base1);
$GrabArm_JointUp.bind($GrabArm_Swivel2);
$GrabArm_JointDown.bind($GrabArm_Swivel2);
}
Add the function
FireSparkGun. Give it the following code.
Code:
void FireSparkGun ()
{
sys.trigger($SparkArm_Fx1);
sys.trigger($SparkArm_Snd1);
sys.trigger($SparkArm_Light1);
sys.wait(3);
sys.trigger($SparkArm_Light1);
}
And add another entity reference to
GrabCanister and
PlaceCanister for the canister tube sound to be triggered. We'll call it
CanTubeSnd.
Then it's just a matter of finding the right line to insert your sound triggers into. I'll save you the trouble. Here's the code.
Code:
void GrabCanister(entity CanPos, entity Can, entity CanTubeSnd)
{
$GrabArm_Base1.moveTo(CanPos);
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Base1);
$GrabArm_Finger1.rotateOnce('45 0 0');
$GrabArm_Finger2.rotateOnce('45 0 0');
$GrabArm_Finger3.rotateOnce('45 0 0');
sys.trigger($GrabArm_JointUp);
Can.move(UP, 30);
sys.trigger(CanTubeSnd);
sys.waitFor(Can);
$GrabArm_Finger1.rotateOnce('-35 0 0');
$GrabArm_Finger2.rotateOnce('-35 0 0');
$GrabArm_Finger3.rotateOnce('-35 0 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Finger1);
Can.bind($GrabArm_Base3);
$GrabArm_Base3.rotateOnce('0 360 0');
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Base3);
$GrabArm_Base2.rotateOnce('0 90 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('90 0 0');
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateOnce('0 -90 0');
sys.trigger($GrabArm_JointUp);
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base2);
}
void PlaceCanister(entity CanPos, entity Can, entity CanTubeSnd)
{
$GrabArm_Base2.rotateOnce('0 90 0');
sys.trigger($GrabArm_JointUp);
$GrabArm_Base1.moveTo(CanPos);
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('-90 0 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base3.rotateOnce('0 -360 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base3);
Can.unbind();
$GrabArm_Finger1.rotateOnce('35 0 0');
$GrabArm_Finger2.rotateOnce('35 0 0');
$GrabArm_Finger3.rotateOnce('35 0 0');
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Finger1);
Can.rotateTo('0 0 0');
Can.move(DOWN, 30);
sys.trigger(CanTubeSnd);
sys.waitFor(Can);
$GrabArm_Finger1.rotateOnce('-45 0 0');
$GrabArm_Finger2.rotateOnce('-45 0 0');
$GrabArm_Finger3.rotateOnce('-45 0 0');
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateTo('0 0 0');
sys.trigger($GrabArm_JointUp);
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base1);
}
Next we'll modify
PosPiecesToFire so it calls
FireSparkGun and triggers its own speakers. Again here's the code.
Code:
void PosPiecesToFire ()
{
$SparkArm_Swivel2.rotateOnce('-90 0 0');
sys.trigger($SparkArm_JointUp);
sys.waitFor($SparkArm_Swivel2);
$SparkArm_Gun1.rotateOnce('-5 0 0');
sys.trigger($SparkArm_JointUp);
sys.waitFor($SparkArm_Gun1);
FireSparkGun();
$SparkArm_Base3.rotateOnce('0 120 0');
sys.trigger($SparkArm_JointDown);
sys.waitFor($SparkArm_Base3);
FireSparkGun();
$SparkArm_Base3.rotateOnce('0 120 0');
sys.trigger($SparkArm_JointDown);
sys.waitFor($SparkArm_Base3);
FireSparkGun();
$SparkArm_Base3.rotateOnce('0 120 0');
sys.trigger($SparkArm_JointDown);
$SparkArm_Gun1.rotateOnce('5 0 0');
sys.trigger($SparkArm_JointDown);
$SparkArm_Swivel2.rotateOnce('90 0 0');
sys.trigger($SparkArm_JointDown);
$SparkArm_Base3.rotateTo('0 0 0');
sys.waitFor($SparkArm_Swivel2);
}
Finally, we'll modify
MechCycle to compensate for the new reference variable
CanTubeSnd. Here's the code.
Code:
void MechCycle ()
{
while (1) {
GrabCanister($Can_Pos1, $Can1, $Can1_TubeSnd);
PosPiecesToFire();
PlaceCanister($Can_Pos1, $Can1, $Can1_TubeSnd);
GrabCanister($Can_Pos2, $Can2, $Can2_TubeSnd);
PosPiecesToFire();
PlaceCanister($Can_Pos2, $Can2, $Can2_TubeSnd);
}
}
And just to make you have to scroll down for an hour here is your final script.
Man its long isn't it?
Code:
////////////////////////////////////////////////////
//
// Setup binds and times etc...
//
////////////////////////////////////////////////////
void setup_objects()
{
// SparkArm Binds
$SparkArm_Fx1.bind($SparkArm_Gun1);
$SparkArm_Light1.bind($SparkArm_Gun1);
$SparkArm_Snd1.bind($SparkArm_Gun1);
$SparkArm_Gun1.bind($SparkArm_Base3);
$SparkArm_Base3.bind($SparkArm_Swivel2);
$SparkArm_Swivel2.bind($SparkArm_Base2);
$SparkArm_Base2.bind($SparkArm_Swivel1);
$SparkArm_Swivel1.bind($SparkArm_Base1);
// GrabArm Binds
$GrabArm_Finger1.bind($GrabArm_Base3);
$GrabArm_Finger2.bind($GrabArm_Base3);
$GrabArm_Finger3.bind($GrabArm_Base3);
$GrabArm_Base3.bind($GrabArm_Swivel2);
$GrabArm_Swivel2.bind($GrabArm_Base2);
$GrabArm_Base2.bind($GrabArm_Swivel1);
$GrabArm_Swivel1.bind($GrabArm_Base1);
$GrabArm_JointUp.bind($GrabArm_Swivel2);
$GrabArm_JointDown.bind($GrabArm_Swivel2);
}
////////////////////////////////////////////////////
//
// Block movement...
//
////////////////////////////////////////////////////
void FireSparkGun ()
{
sys.trigger($SparkArm_Fx1);
sys.trigger($SparkArm_Snd1);
sys.trigger($SparkArm_Light1);
sys.wait(3);
sys.trigger($SparkArm_Light1);
}
void GrabCanister(entity CanPos, entity Can, entity CanTubeSnd)
{
$GrabArm_Base1.moveTo(CanPos);
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Base1);
$GrabArm_Finger1.rotateOnce('45 0 0');
$GrabArm_Finger2.rotateOnce('45 0 0');
$GrabArm_Finger3.rotateOnce('45 0 0');
sys.trigger($GrabArm_JointUp);
Can.move(UP, 30);
sys.trigger(CanTubeSnd);
sys.waitFor(Can);
$GrabArm_Finger1.rotateOnce('-35 0 0');
$GrabArm_Finger2.rotateOnce('-35 0 0');
$GrabArm_Finger3.rotateOnce('-35 0 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Finger1);
Can.bind($GrabArm_Base3);
$GrabArm_Base3.rotateOnce('0 360 0');
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Base3);
$GrabArm_Base2.rotateOnce('0 90 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('90 0 0');
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateOnce('0 -90 0');
sys.trigger($GrabArm_JointUp);
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base2);
}
void PlaceCanister(entity CanPos, entity Can, entity CanTubeSnd)
{
$GrabArm_Base2.rotateOnce('0 90 0');
sys.trigger($GrabArm_JointUp);
$GrabArm_Base1.moveTo(CanPos);
sys.waitFor($GrabArm_Base2);
$GrabArm_Swivel2.rotateOnce('-90 0 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Swivel2);
$GrabArm_Base3.rotateOnce('0 -360 0');
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base3);
Can.unbind();
$GrabArm_Finger1.rotateOnce('35 0 0');
$GrabArm_Finger2.rotateOnce('35 0 0');
$GrabArm_Finger3.rotateOnce('35 0 0');
sys.trigger($GrabArm_JointUp);
sys.waitFor($GrabArm_Finger1);
Can.rotateTo('0 0 0');
Can.move(DOWN, 30);
sys.trigger(CanTubeSnd);
sys.waitFor(Can);
$GrabArm_Finger1.rotateOnce('-45 0 0');
$GrabArm_Finger2.rotateOnce('-45 0 0');
$GrabArm_Finger3.rotateOnce('-45 0 0');
$GrabArm_Base1.moveTo($Arm_Pos1);
$GrabArm_Base2.rotateTo('0 0 0');
sys.trigger($GrabArm_JointUp);
sys.trigger($GrabArm_JointDown);
sys.waitFor($GrabArm_Base1);
}
void PosPiecesToFire ()
{
$SparkArm_Swivel2.rotateOnce('-90 0 0');
sys.trigger($SparkArm_JointUp);
sys.waitFor($SparkArm_Swivel2);
$SparkArm_Gun1.rotateOnce('-5 0 0');
sys.trigger($SparkArm_JointUp);
sys.waitFor($SparkArm_Gun1);
FireSparkGun();
$SparkArm_Base3.rotateOnce('0 120 0');
sys.trigger($SparkArm_JointDown);
sys.waitFor($SparkArm_Base3);
FireSparkGun();
$SparkArm_Base3.rotateOnce('0 120 0');
sys.trigger($SparkArm_JointDown);
sys.waitFor($SparkArm_Base3);
FireSparkGun();
$SparkArm_Base3.rotateOnce('0 120 0');
sys.trigger($SparkArm_JointDown);
$SparkArm_Gun1.rotateOnce('5 0 0');
sys.trigger($SparkArm_JointDown);
$SparkArm_Swivel2.rotateOnce('90 0 0');
sys.trigger($SparkArm_JointDown);
$SparkArm_Base3.rotateTo('0 0 0');
sys.waitFor($SparkArm_Swivel2);
}
void MechCycle ()
{
while (1) {
GrabCanister($Can_Pos1, $Can1, $Can1_TubeSnd);
PosPiecesToFire();
PlaceCanister($Can_Pos1, $Can1, $Can1_TubeSnd);
GrabCanister($Can_Pos2, $Can2, $Can2_TubeSnd);
PosPiecesToFire();
PlaceCanister($Can_Pos2, $Can2, $Can2_TubeSnd);
}
}
////////////////////////////////////////////////////
//
// MAIN
//
////////////////////////////////////////////////////
void main ()
{
setup_objects ();
sys.wait(3);
MechCycle();
}
Now you save your script and run the map. Ta Da! Here is the exact same script you started with... Ahem.. I mean look! All that hard work paid off!
ConclusionThat pretty much sums up what I know of scripting as a whole. I'm hoping that some of you out there will be on my level soon and we can start tackling ideas together. If not, run over this tutorial a couple of times. You should get it.
Got any questions? Ask them.
Nope, everything is where it's supposed to be here, unzipped into the base folder, I double checked all the indvidual subfolders and it looks proper. The only thing I can think of is that I have unpacked all my pak files as well in order to explore the file structure, so that might create some conflicts. And yes, everything else (map, script) works fine.
Ah! It's because you unpaked your pk4s. When you do that it causes a conflict with new content.
It's best if you want to look at the files within the PK4s that you extract them to a separate directory.
In other words, when you extracted Doom 3's pk4s into your base directory you disabled your ability to load custom content. When Doom 3 loads it sees all the files you extracted in their appropriate directories and begins to load them as a mod, even though it's just the game's vanilla content.
Obviously if the map and models from the tutorial load but they don't display a texture then doom 3's being rather quirky as to what will work and what won't.
What I will tell you is that it works without the pk4s extracted and I don't plan on unpacking my pk4's and testing my tutorials with them just so you can "have it your way". To put it frankly, this ain't Burger King.
I was asking that because everything I know about the engine and pak files (from older id engines) says that unpacked files override the pak files, but your files do not replace any existing files and so there should be no difference if the files are in paks or in folders, so I was just wondering where exactly the conflict occurs (as in, doom3 will not load the mtr file for some reason, or whatever other thing is causing this behaviour?).
I'm not sure why it doesn't work. Technically, it should. But the only way to find the actual code reponsible for the problem is to read every script, def, and otherwise uncompiled bit in the game to narrow the problem down.
My hands are full updating definitions, revising tutorials, ect... I simply don't have the time.
All I can do is make educated guesses and since I know on my end that the PK4s are not extracted and they are on yours, it's a relativley safe assumption that the extracted PK4s are your problem.
If you could just work with me by cleaning out your base directory and trying the tutorial that way, I'd honestly appriciate it. If it still doesn't work then I'll have a reason to troubleshoot the issue further.
I think I did that just now, but there's no change, I'm going to try reinstalling the whole thing and try it on a clean install.
When I said that I though I had cleaned it out I meant that I had the pak files in the base and I thought I had deleted all the individual files that came out of them, but I wasn't sure, hence I am reinstalling.
Done reinstalling, on a completely clean D3 install it works (I think, it has shadows and is solid grey, with the "fingers" solid white).