This tutorial is intended as a follow on to my previous "Writing your first BHAV" tutorial. If you have a pre-existing knowledge of BHAVs then you should be fine, however if you are not confident with BHAV coding then I recommend reading through that one first. I'd also recommend playing around with the material you learned there for a bit too, and consolidate the information you picked up by doing it.
You might also consider looking through the "Data Types for Simantics Users" article. While it is not a strict pre-requisite for this tutorial, it includes helpful information on debugging code, and background which might help make the contents of this tutorial make a little more sense.
Based on the material covered in the "Writing your first BHAV" tutorial, make a clone of the "On A Pedestal" statue, and give it a new interaction called "Talk". In the interaction, make your sim move to the front of the statue, play the "a-soc-talktalk-loop1xtalkme" animation, then return true. If you can't remember how to do any of this, then go back and take another look at the original tutorial for detailed instructions, because you're probably not going to be able to make it through the rest of this tutorial!
One thing to note when you're working with this statue - the cloning process for this object pulls only one pie menu resource, which is not actually the correct one. You should delete that one, and import both the pie menu strings and the pie menu functions from the semi-global library.
Have you ever noticed how, if you instruct a sim to use a particular object often enough, they'll start to choose to use that object autonomously? This is because of a system of relationships built into the game. You'll also notice that objects in the sims can be marked as "in use", in which case no other sims can use them at the same time.
The global function "Standard Entry" is designed to manage both of these. When it is called, it marks the object as currently in use and updates the relationship between the sim and the object. To turn this off again, you just need to call the "Standard Exit" global function. Let's add this to the statue's code now.
The Standard Entry call should be placed at the point in the interaction where the sim is just about to start using the object. In the statue's code, this should be just after the Move to Relative Position call but before the Animate Sim call. Insert a new call to global 0x110 (Standard Entry). This function doesn't require any parameters, so you can just set them all to 0x00.
Now, anything you turn on you have to turn off again, so every Standard Entry must have an equivalent Standard Exit. Go right to the end of the interaction, after the animation has finished, and add another command. Set this one to call global 0x0111 - Standard Exit. This function can also have all its parameters set to 0x00.
Before we test this in game, we want to make sure that sims don't use the object if it's already marked as "in use". This doesn't mean just adding a guard function though. Think about the way that a bed works in the sims. You can always direct a sim to use it, and the sim will go over to it as though they are going to do as instructed. However, if it is already in use, they will stop at this point and complain, then exit the interaction. That means that the code to check whether an object is in use or not needs to go after the sim has walked to the object, but before they have entered the interaction.
Insert a new command just before the Standard Entry call, and set it to the Expression command (opcode 0x02). Use the Instruction Wizard to select "Stack Object's - flags - Flag Set? - Literal - in use". This makes this node into a decision point asking "is this object in use?". If this node returns true then the object is already in use, so end the interaction by returning false. If this node returns false, then the object is free and you can go on to enter the interaction.
Try your statue out in game now. Make sure that everything works, and that you don't get any unexpected errors. (Remember - if you're testing BHAV mods, you should be doing it with testingcheats enabled!)
A couple of small notes about Standard Entries and Exits. The commands described above are only for single tile objects. If your object has multiple tiles, then you will need to use globals 0x116 (Standard Entry - Multi Tile) and 0x117 (Standard Exit - Multi-Tile). These do require parameters which can vary depending on how you're using them. Investigate entry and exit calls in similar objects to get the correct parameters if you are using these commands.
You should also note that there is a global BHAV called "In use?", 0x0102. This global works exactly the same way as the expression command version that I described above, and either can be used.
You already know how to award skills to sims using the Expression command; you just add points to the sim's skill value attribute. However, this increases skills in big bursts, it's not the way sims normally earn skill points in game. When they earn skills, you need to create a skill meter above the sims' heads, and have the skill build up gradually and continuously over time. Fortunately, there are global functions to help us do this too!
Much like entering and exiting above, skills need one function call to start them off, and a second to end them. Find the point where we want the sim to start building skills. This should be after the Standard Entry and before the Animate Sim. Insert a new command here, and set it to "Skill - Start" (global 0x1D7). You'll see that it takes four parameters.
A parameter is a value that is given to a BHAV by the BHAV that calls it, specifying something about the way that it is supposed to run. For example, in the "Skill - Start" global, it takes parameters that specify what skill is being earned, how fast it is being earned, which object the sim is using to earn the skill, and if there are any personality bonuses that need to be applied (ie - outgoing sims might get a bonus in charisma building). The function calling this BHAV can specify all of these values, and then the BHAV which is called will modify its behaviour based on them. If you want to know how it does this, then read "Data Types for Simantics Users".
Open the instruction wizard, and you'll see boxes where you can select values. Make sure that "New Format" is selected at the top, so you can choose what sort of values you are passing to the function. (If "Old format" is checked, all values will be treated as literals).
The first parameter is the identifier for the sort of skill your sim will be earning. In this case, we're using skill 0xB - Charisma. By specifying a "Literal", we are saying "the actual value 0xB". If we wanted to, we could tell it to use a value that was stored in a variable or in a constant, but for now it's easier to stick with a literal. The second parameter says how fast the sim is earning the skill. Higher values mean your sims will learn the skill faster. In this case, we've just picked a fairly random value of 0x70. The third parameter says what object is causing the skill build. We've specified the "Stack Object ID". The Stack Object ID is a special variable that every function has. For an interaction, the Stack Object ID is initially set to the object the user clicked on to start the interaction, so in this case it will contain the ID of the statue we've been modding. The last parameter is asking if there are any personality bonuses for the skill building. In this case, we're just going to assume that there aren't and setting this value to 0x0.
Because we started the skilling, we're responsible for turning it off again. Go to the end of the function, just before the Standard Exit call, and add a new command. Set it to call global 0x01D8 - "Skill - End". Don't worry, this one doesn't have any parameters, so once it's in you can just leave it as it is.
To really take advantage of the skill earning, set up a loop around the animation. Make sure you don't include the start or stop functions in this loop though, just the animation. Otherwise the sim's progress meter will be appearing and disappearing like crazy!
Commit your changes and try the object in game. Your sim should start earning charisma skill as it talks to the statue now!
The skill gains you've got so far will work fine in game, and for some things they're completely sufficient. However, most skill gaining objects let you know every time the sim completes a point by bringing up a dialog window.
The way this window gets called is through a particular functionality in the skill gaining code, which looks in the skilling object for a function with the name "CT - Skill Gain Dialog", and runs that function every time the progress meter fills up and turns over.
To make it easy on ourselves, we're going to just copy this function from an object which already has one. Save your current object, and close it, then make a clone of the easel. Save this clone somewhere, you will need it again later.
Look through the list of BHAVs the easel clone has, and find the "CT - Skill Gain Dialog" BHAV. Right click on the name of this in the file list, and select "Extract". This will prompt you to save the BHAV somewhere - put it somewhere easy to find for now.
The "Extract" option copies everything in that BHAV to a temporary file, so you can load it into another package. That's exactly what we're going to do now. Close the easel clone, and open up your new hacked statue again. Right click somewhere in the white space of the file list, and select "Add". Find the extracted BHAV, select it, and press "OK". The new BHAV should show up in your package's list of files!
Because you copied this file in from another object, it may be using an instance number which is already in use in your object. In the case of the easel and the statue, this shouldn't be a problem. However, if you're using any other objects as sources, you need to check this now. If the CT function does have the same instance number as another BHAV, then change it now. Because the game is looking for the CT function by name, it doesn't matter what instance number is used, so long as it is in the 0x1000 range and doesn't conflict with any other BHAVs in the package.
Now, if you open the CT function, you'll see that it has a "dialog" command at the end. That's the command that brings up the little window each time your sim earns a skill point. Click on that command and look at how it gets decoded.
That's probably not the message you want, it's not even the message that showed up for the easel! The message it says it's going to display is unexpected - "No one can "own" this object.". That's because the messages that get displayed in dialog boxes are stored in a particular text list, just like the names of animations are stored in a particular text list. The text list that dialogs look for has an instance number of 0x12D.
Save your package, and open up the easel again. Find the text list 0x12D, and open it. In line 0x04, you should see the text "It's Art! $NameLocal:0 has gained a point of Creativity Skill from the $NameLocal:1." That's the text that the easel normally displays when a sim earns a point. Extract the whole text list to the same place you extracted the CT function earlier, then close the easel. Go back to your statue and import the text list the same way you did with the exported BHAV earlier.
Now your object has a text list 0x12D, with dialog messages. If you go back to look in the Dialog line of the CT function, you should find that it's displaying the line from the new text list. That's a big improvement, but it's not quite right yet. The statue is supposed to build charisma, not creativity, and the "It's Art!" doesn't really make any sense. Go back into the text list and change that line in the text list. Delete the part that says "It's Art!" and change the "Creativity" to "Charisma". You can make the text more elaborate if you want, but make sure that you leave the $NameLocal:0 and $NameLocal:1 parts as they are. When the dialog gets run, the $NameLocal:0 will have the name of the sim substituted in, and $NameLocal:1 will have the name of the statue. It can do this because local 0 and local 1 have the sim and the statue's object IDs respectively stored in them, thanks to the first three lines of the CT function.
If you are willing to go an make the same change for each of the other languages, do that now (just like in the pie menu strings). If not, press the "Default lang only" button, which will make all language versions use the translation you entered.
Commit and save the changes to your object, then try it out in game. The next time your sim earns a skill point, the new dialog text should appear. Well done!
Skilling is hard work for sims, and if they're not in a good mood they can and do refuse to do it... At least, they do for most Maxis skill objects, but they don't for your new object. That's because you have to add code to your object to make it happen. Fortunately, there's a global function which makes this really easy to do!
Add a new node to your interaction, right at the start, and set it to use the global 0x019F, "Skill - Exit Skill Object". There is only one parameter for this, which is the minimum mood value that a sim will still perform the action. Set it to literal 0x0 for now. If this function returns false, then you want the interaction to continue. If the function returns true, then the sim is not in a good enough mood for this and you need to let them exit the interaction.
Now if you try to get a sim to skill when they're not in the mood, they won't. There's one more important thing you'll want to do with this function though - make sure that sims will break out of the skilling interaction by themselves if they have had enough. Add another node, inside the loop this time, and set it to global 0x019F. The parameters are the same as above. Similarly, if the function returns false the loop should continue, and if the function returns true, then you should exit the loop. Make sure that you point to the right node when you enter the loop - you still need to turn off everything you turned on before you exit the interaction completely!
Now if your sim's motives drop too low during their skilling, they'll quit - just as they should do!
Not all sims are intellectual equals. Some sims learn some things faster or slower than others. This is dependent on a few different factors, but most of them can be calculated automatically using global functions.
Just before the "Skill - Start", add a new node. Set it to use the global function "Get in Temp 0 - Personality Skill Bonus". Because some personality types are better suited to some types of skills than others, this function will calculate the skill bonus the sim should get for a particular skill, then place that value in temp 0. This function takes two parameters. The first is concerned with which personality type the bonus is geared to. Let's set it to literal 0x6 - outgoing. The second parameter is the bonus your sim can earn as a percentage. Set that to 0x02EE. If you're curious, I took both of these values from the "Execuputter" career reward. Career rewards are excellent examples of skill building code!
Finding the personality skill bonus by itself isn't very helpful though, so you should add another node directly after and set it to run the "Get in Temp 0 - Skill Gain Speed." global function. This function takes the value in temp 0 (which you just set) into account as the personality bonus, as well as any special skill gaining boosts that your sim has (including the fast learning helmets etc), and calculates the rate it which the sim should gain the skill. The first parameter for this function asks for a base rate of gain for this skill. I'd suggest copying this value from another object which has about the right rate of skill gain. The execuputter uses the literal value 0xFA, so enter that in for the first parameter. The second parameter is for the personality bonus. As we already have this stored in Temp 0x00, then set the second parameter to point to Temp 0x00. The "Get in Temp 0 - Skill Gain Speed" function overwrites the value in temp 0, but that's fine because we've now used the value we put in there earlier and we don't need it any more.
Now that we have a proper, correctly calculated rate for the sim to gain skill, let's make the "Skill - Start" function use that value. Change the second parameter to use Temp 0x00. This value tells the game that instead of using a literal value for the rate of skill increase, we're going to use the value stored in temp variable 0.
Commit and save, then try it out in game. There might not be a very obvious effect, but trying getting two of the same objects in game, each with a sim trying to earn skill points. Now put the skill gain helmet on one of the sims. You should see that sim gain skills much faster than the other. Congratulations, you now have skills set up to be work with with in-game skill bonuses!
Note that there are other, equally valid ways of managing motives. You'll see several different techniques in Maxis objects, all equally valid. If you're feeling adventurous, feel free to investigate these more closely!
Compared to earning skills, managing motives properly is really quite easy. This time there are primitive functions which make motive management automatic and smooth.
Find the place in the BHAV where you want the sim to start gaining motive points. This definitely should be after the Standard Entry, but to keep things neat let's put it in just after the skilling has started. That way if the sim refuses to use the object we won't need to run any motive management code.
The first thing to do is to make sure that there are no left over motive changes happening from something else the sim has been doing. To do this, we set all the motive changes to default on the active sim, using "Set Motive Change" primitive. There is currently no instruction wizard for this primitive, so just enter these parameters directly (they're always the same for resetting motives):
Now that we know we're working with a clean motive palette, let's set the actual motives. Add another "Set Motive Change" command, and make sure that it runs before the animation loop gets started.
There are three parameters that are important to this command create motive boosts. We need to specify which motive to boost, how fast to boost it, and how high the motive should be allowed to get.
Set the first two operand boxes to 0x07. This tells the game that we're going to provide literal values for the rate of skill increase, and for the maximum value the motive boost can reach. The third operand box selects which motive is going to be getting a boost. Set this to 0x0E, which is the social motive. Leave the fourth motive box as 0. This field controls a flag field which isn't needed in this case. The next two operand boxes (the fifth and sixth) specify how fast the skill should be gained, as a measure of points per hour. Remember that these fields are "little endian", which means that if you enter 0x23 and 0x01 in the two boxes, that would actually be read by the game as 0x0123. You can use whatever value you want here, but I'd suggest setting it to something like 0x0020.
Finally, the last two operand boxes in the top line specify the maximum value that the motive should be allowed to reach. Because we want sims to be able to max out their social need with this object if they use it for long enough, we'll set it to 0x0064, which is the hex equivalent of 100 (the maximum value for any motive. Remember that these two operand boxes are also little endian, so you will need to re-arrange the numbers so they work).
That's all there is to it! When your sim hits the "Standard Exit", they will end their motive gain by themselves. If you wanted to end it yourself during the interaction, you could do that by using the code provided earlier in this section describing how to set all the motive changes to default.
Try your object in game, and see how it works. Change some things, see how that affects your object. If something isn't working right, compare it to the sample package. But don't be afraid to break it! You'll learn a lot by breaking something then fixing it again.
Keep trying things out, and copying things from other objects, and hopefully make something really cool to share! Have fun!
This site is not endorsed by or affiliated with Electronic Arts, or its licensors. Trademarks are the property of their respective owners. Game content and materials copyright Electronic Arts Inc. and its licensors. All Rights Reserved.