Echo
25th Dec 2006, 08:30 AM
TS2 Data Types for Simantics Users
Overview
If you've never made a custom BHAV before, this is not the document for you. I'd suggest one of the Beginners' Tutorials (http://www.sims2wiki.info/SimAntics#Beginners.27_Tutorials) instead. The information here will probably only be helpful for those people who have been playing with BHAVs for a while, but who want to get a better understanding of the different ways in-game data can be stored and changed using BHAVs. It starts out with fairly basic stuff, but gets a little more complicated near the end.
At its simplest level, almost all programming is about manipulating data in some way or another, and SimAntics is no different. If you really want to create extraordinary hacks, it's important to understand the way the game stores this data, and when and how you should use it.
Everything written here is entirely based on observation of the game's behaviour, error logs, and general software architecture theory. If you find anything incorrect or unclear, please let me know asap and I'll try to correct it.
Enjoy, and good luck!
Literals, Variables and Constants
Before delving into the specifics of the Sims data model, it's important to have a very general grasp of data structures and their theory. If you have previous programming experience and can confidently say that you understand literals, variables and constants, please feel free to skip ahead.
Calling something a "literal value" is a fancy way of saying it's a regular, normal value. For example, the number 5 is a literal. The number 3.142 is also a literal. The phrase "the cat sat on the mat" is also a literal. This may seem a very odd idea, but it will make more sense shortly. At this point in time, just understand that the equation:
1 + 5
can be read a bit like this
literal(1) + literal(5)
which says "find the value of literal 1 plus literal 5".
For the purposes of my ongoing analogy, a literal is like a number that is written on a piece of paper in pen. Fair enough?
Now, variables are a bit different. Variables are names we give to a place where a literal can be stored. If you're imagining a literal as a value on a piece of paper, then imagine a variable as a plastic sleeve. You can put the literal into the plastic sleeve, you can take the literal out of the plastic sleeve, and you can put a different literal back into the plastic sleeve afterwards. But here's the clever bit - you can also put a label on that plastic sleeve. For example, you could label the plastic sleeve "Number of days until my birthday", and put a literal into it saying 50. If someone asks you "how many days until your birthday", then you can find the right plastic sleeve, then tell them what literal is stored in it. Tomorrow morning, you can take out the literal value you put in there, find the literal that is one lower, then put that into the sleeve instead. Now if someone asks you "how many days until your birthday", you can follow the exact same set of steps to find out, but the value you give them back will have changed.
In the Sims, variables don't always have such sensible names (although you can give them names if you wish). By default, they just have numbers. So if you have three variables, then you can access the values in them by number. For example:
variable(0) := literal(1)
variable(1) := literal(5)
variable(0) + variable(1)
is much the same as the equation listed under "literals" above. Note that the ":=" symbol means "gets the value of". The first line says "variable 0 gets the value of 1". The second line says "variable 1 gets the value of 5". The third line says "find the value of what's in variable 0 plus what's in variable 1".
All good? Good. If you're fine with that, then you should be okay with the rest of this document.
Finally, constants are special types of variables. You can only put a literal into them once. Then they can never be changed (ie - they can not "vary", they are "constant"). Instead of being like a plastic sleeve, this is more like encasing the literal in a layer of perspex. You can pick the value initially, but once it's in the perspex you can't change it again. For example, you might have a constant "how many days in January", which would hold the value 31. This shouldn't ever change, but putting it in a constant makes sure that you can always get at it when you need it.
So that's the three simplest ways of representing data that you will encounter when you're programming, and you'll find that most common programming language around today use them. So, if you want more information about how they work then documentation exists for it all over the Internet.
Now we get to the interesting bit - how this all works in the Sims.
Using built in variables
If you've done any BHAV tutorials before, you're probably familiar with the "Expression" command. This is a primitive command (ie - it's built into the game at the lowest level) which lets you manipulate variables directly. For example, you may know by now that if you enter:
Expression (My motive 0x0008 (Hygiene) := Literal 0x64)
in an interaction, then it will make the current sim's hygene value get set to maximum; 100 points (0x64 is hexadecimal for 100). But take a closer look at it given what you now know about literals and variables. Obviously, the second half is a literal, it is the literal value 0x64 or 100. But what about the left hand side? That has got to be some sort of variable it's storing it in. The variable is called "hygiene", and we're putting the literal value 100 into it.
Motives are one of the groups of variables that all sims get when they are created. Person data and Object Data are also groups of variables that all sims have. You can read the values in them, and you can write new values into them. Because they are variables, you can do all sorts of useful stuff. For example:
Expression (My motive 0x0008 (Hygiene) := My motive 0x000F (Fun))
will set the current sim's hygiene value to whatever their fun value is.
You can also use the Expression command with variables and constants to make decisions. For example:
Expression (My motive 0x0008 (Hygiene) > My motive 0x000F (Fun))
asks "is the value in My motive 0x0008 bigger than the value in My motive 0x000F?". If this is true, then the "true" execution path will be followed. If it is false, then the false path will be followed.
Objects also have similar groups of variables. The Object Data variables that exist in sims also exist in objects. However, the Person Data and Motives groups do not exist in regular objects (obviously). If you play around with the instruction wizard for Expression, you can see what sort of values you can manipulate.
Finally, there is a group of variables that cover information about the lot as a whole. These are called "globals". For example, there is a global variable for the time of day. They can be very handy to read, although you should be very careful when writing to them because they can affect a lot of different things.
Using constants
Constants in the sims are stored in BCON files. Most of the time, they are used to store values that get used in a lot different places in an object. The simple reason for this is that if you initially want the value 0x3 to be used in eight different BHAVs, then a few weeks later decide that 0x3 is too low and you want to change it to 0x5, it's a lot easier to change one number in a BCON file than it is to find all the places in the eight BHAVs where you used the number 0x3 and change them there.
Constant values get stored one to a line in a BCON file, but can be used in almost all the same places that literals can be used. You can specify which constant you're looking for using the instance number of the BCON file (eg - 0x1001) and the line number within the BCON file of the particular constant you want (eg - :0x00). To add constants to your project, you just have to add a BCON file to your package, and give it an instance number in the range 0x1000 to 0x1FFF that hasn't already been used by an existing BCON.
You can also name the constants in your BCON files if you want. Simply open your BCON and click the "Make Labels" button. This will create a new file in your package called a "Behaviour Constant Label". TRCNs are linked to BCONs by their instance number, so TRCN with instance 0x1000 holds the names of the constants defined in BCON 0x1000.
If you open this TRCN file, you can change the labels for each of the lines in your BCON.
Using local variables
Local variables, or "locals" are variables with a very short life span. They are created when a BHAV first starts, and as soon as the BHAV returns it is destroyed. However, you can use it to store and change values for as long as that BHAV runs.
This may seem fairly useless, but locals can be extremely useful. Use them to keep track of how many times you've gone through a loop, or to hold the object ID of a particular object so you can use it later in the function. They're really very handy!
If you want to use locals in your function, then you need to make sure they are created when the function is first called. You can do that by entering the number of local variables you will need into the "Local Var Count" box in the BHAV editor. Once that is done, you can use those variables in any way you wish within the function. It's important to remember that while the number in the "Local Var Count" box is the number of variables you will need, the identifying numbers for the variables start at zero. That is, if you say you need one local variable, it will be called local 0x0. If you say you need two local variables, you will get local 0x0 and local 0x1.
Passing variables to functions
A parameter is a value that you can send to a BHAV that you are calling, so that BHAV can use it like a local. For example, if you were writing a BHAV to give skill points to every sim on the lot, then you might have a parameter which said which skill type to give points to, and another to say how many skill points. That way, you could reuse that function for all sorts of different skills and values.
The function calling a BHAV can specify all of these values using parameters, and then the BHAV which is called will modify its behaviour based on them. These parameters are entered into the "operands" boxes. For any non-primitive functions, the order of the values follows one of two patterns.
The first option allows only literal values. If the parameters are being read this way, then two operand boxes represent one parameter. The first parameter goes in boxes one and two, the second parameter goes in boxes three and four, and so on. The only hitch is that the two boxes need to be read in reverse order. For example, if boxes one and two read 0x01 and 0x23 respectively, then the value of parameter one is 0x2301. (There is a very good reason for this which I won't go into now. If you're curious, ask some time!)
The second way to pass parameters is even more useful though, because it allows you send values from within variables. If the thirteenth operand box (fifth on the second row) is set to '1', this tells the game that instead of using two operand boxes per parameter, it should use three. In this case, the first of each set of three boxes is the "data owner", and the remaining two represent the data value. The "data owner" is the part that says what sort of data to use - literals, locals, constants, sim and object attributes, etc. The remaining two boxes specify the data value, that is, local *0x00* or, literal *0x05*. Like the first way of passing parameters, the two boxes need to be read in reverse order to get the actual value.
Using parameters in your own functions
Making your function use parameters is almost exactly like making your function use variables. You have to make sure that the function knows that it will have parameters, so you need to enter the number of parameters into the box labelled "Arg count". Once you have declared them in this box, you can use them anywhere in your code where you would use locals, but instead of selecting "Local 0x1" you would select "Param 0x1". Like locals, parameter labels start counting at 0x0.
Naming locals and parameters
In the same way that you can label each record in a BCON, you can also label each parameter and local you create for your functions. Simply open up the BHAV you wish to provide labels for, and click the "Make Labels" button. This will create a "Edith Simantics Behaviour Label" file, or "TPRP". You can then use this file to enter names for your locals and parameters, which should show up when you're reading through your code. This can be extremely useful if you have a few variables in your code - you're much less likely to forget what is going on!
Creating new attributes
If you've got a cloned or custom object, you can create new attributes for it, or change existing attributes. All of the attributes for an object are stored within the package, in one of two places. The first is in the OBJD file for the object's master tile. Under the "Attribute Number" field in the OBJD is a number representing how many attributes the object has.
The second place attributes are stored is in the text lists. Text list 0x100 contains one entry for each attribute the object has. If you add records to this text file in your package, you should get more attributes.
In most situations, the actual number of attributes in the object will be whichever is greater out of the OBJD record and the count of records in the text list. In some very rare circumstances though this may not be the case, so it's not a bad idea to make sure the values match across both locations.
Understanding the call stack
I'm afraid we're going to lapse into a bit more programming theory here, and for that I apologise. However, it's important to know a bit about how the game keeps track of what's running at any particular time, because a lot of other things are related to it.
The first thing to understand is what a "stack" is in computing terms. If you can picture a stack of open books, you're pretty much on the right track. A stack is a way of representing all the things that have happened, the order in which they happened, and what thing is currently happening.
For example, say you're reading a book, and you come across a word you don't know. You have to look that word up, but you don't want to lose your place in the book. So what you do is leave the book open on the right page, then grab a dictionary, put the dictionary on top of the open book, and look up the word. Once you've found the word, you can close the dictionary and put it away, the go back to reading the first book right from where you left off. The other thing you'll realise about this sort of stack is that you can only see what's in the top book of the stack. You can't see the text in the bottom book, because there are other books in the way.
A call stack is a lot like this, except instead of books it has functions that are being run. Every time a BHAV gets called, it gets put on the top of the call stack. The space on the stack that each BHAV takes up is called a "frame". The game can only see the frame (BHAV/book) on the very top of the stack, so it will only run code in the top BHAV. Consequently, it starts running that code from the beginning. Now, if that code makes a call to another BHAV, then the game will load a copy of the BHAV which is being called, and put that one on top of the stack. Because it's on the top of the stack now, the game will start executing that one until it either reaches another function call (in which case it will stack another BHAV on top and run that) or until it returns.
When you return from a function, you're saying "I'm finished with this BHAV", and so the game will take it off the top of the stack and get rid of it, revealing the BHAV which called it. The game will then keep going from wherever it left off. If your BHAV returned "true", then when the calling function starts up again it will follow the true path. If your BHAV returned "false", the calling function will follow the false path.
"Me" and the "Stack Object"
In the game, all objects (including Sims) have their own call stack. Any code that runs can tell which object's stack it is running in by looking at "Me" or "My". For example, "My 0x000B (Object ID)" is the variable holding the object ID of whichever object owns the stack the code is being run in. If you have an object which runs an "init" function, then that function will be run in its own object's stack. That's why "My" in an init function refers to the object itself. However, if you have code in an interaction, that code is run in the stack of the sim interacting with the object. That's why "My" in interactions refers to the sim. Because Me/My is always the owner of the stack, it's not something that you can change very easily in a function.
"Stack Object" behaves quite a bit differently though. The "Stack Object ID" is a variable that is created when every BHAV begins execution, and is local to that function. However, unlike most local variables, when the function starts this variable gets defaulted to the value that was in the "Stack Object ID" variable of the BHAV that called it. For example, if function "FirstFunction" does something like:
Stack Object ID := literal 0x5
Call SomeOtherFunction
Then when SomeOtherFunction starts, its stack object ID variable will be set to 5. This only works one way though! Once SomeOtherFunction starts, Stack Object ID behaves like a local. The function can change the Stack Object ID all it wants within its own code, but when it returns those changes will not appear in FirstFunction. FirstFunction will still have the value 0x5.
But why bother with Stack Object ID if it's really just a fancy version of a local variable?
Whenever an object is created in the sim game, it is assigned a number. This is called its object ID, and that number is unique to that object within any particular lot. Say you get a new sim on the lot. The game will automatically give it an object ID - let's say, 0x30. If you ran this code:
Stack Object ID := 30
Then the Stack Object would now be that sim. That means that if you looked at the variable:
Stack Object's motive 0x0008 (Hygiene)
you would get the Hygiene value of that sim. Handy, huh?
When you first enter an interaction, the stack object ID is set to the object that you clicked on to start the interaction. That means that if you don't change the stack object ID in the code, the stack object will point to the object the sim is interacting with.
A great many commands work based on what the stack object ID is. Others will store values into the stack object directly. For example:
0: Stack Object := 0 [True: 0x1, False: Error]
1: Set to Next (Object in room) [True: 0x2, False: Return True]
2: Go to Relative Position (Anywhere near, Stack Object) [True: 0x1, False: 0x1]
"Set to next" changes the value in Stack Object ID to the next highest value that meets the criteria that you provide, for example, objects in the same room as the sim. This piece of code will have sims walk to every object in their current room, one after the other.
A quick side note: an object's ID is only guaranteed to stay the same from the time you enter the lot until the time you leave it. While the numbers don't necessarily change between loads, they can do. Also, a Sim's object ID on one lot is not necessarily the same as it is on another lot. If you want to keep track of Sims uniquely, use their "Neighbour ID" number instead.
Using temps
Temps are stored at the stack level, like attributes, but they are only meant to be used to hold things temporarily.
Now that's not very useful if you're storing values just for use inside a function - it's generally better to use locals for that (although there are exceptions to this rule). Since they're only meant for temporary use, they're no good for holding values you want to keep for very long either. But they do have one important use - transferring values between different function calls.
If you use parameters then it is possible for a calling function to give values to the function it is calling, but that only works one way. The only way for a function to return a value back to its calling function is for it to store that value in a stack level variable, so that once the function has finished and returned to its calling function, the calling function can retrieve the value from the stack-level variable. This is the ideal use for temps.
As it happens, a lot of global functions use this technique to make coding complex behaviours easier. For example, if you call global 0x403 ("Get in Temp 0 - Aspiration") then the aspiration of the "me" sim will be stored in the temp 0x0 variable of your stack. Global 0x151 ("Get in Temp 3 - My Handedness") will store a different value in temp 0x3 depending on whether your sim is left or right handed. (As a side note to this, the "Animate Sim" primitive reads the value in temp 0x3 to determine whether an animation should be flipped. That means that if you call "Get in Temp 3 - My Handedness" then run an animate sim command straight after, then a left handed sim will use their left hand and a right handed sim their right!)
Reading and writing temps is much the same as attributes. Just select "temp" as the data owner, rather than "attribute"! Remember that they are really only good for temporary storage of data though, and any time you call a global, semi-global or primitive function it is possible that it might change the values without warning. You also mustn't assume that the value will stay the same after your function has finished. If you do use temps to communicate values across functions, and you want to keep those values or reuse them later, it's a very good idea to copy them into locals or attributes until you need them to be in the temps again.
How this relates to your error log
Once you've made sense of all of the above, then the error log will become your instant best friend when you're trying to edit BHAVS.
If you have a BHAV which is causing an error in game, then the game is able to create an error log telling you all sorts of information about what state the game was in when it happened. If you're not sure how to create an error log, check out this thread:
http://www.modthesims2.com/showthread.php?t=198229
The first (and probably most useful) section of an error log tells you what has actually gone wrong, and roughly where. The Object id row specifies which object owns the stack running the code with the error. That is, the id of the object treated as "Me" in the code. The "name" field directly following this holds the name of that object.
The stack size states the number of frames currently stored in the stack. Don't worry too much about this line, it's not very helpful for debugging.
The next line, "Error", will give you a hint as to what has actually gone wrong. There are a limited range of messages that can be reported, but most of the time it's still a good indication of what has caused the problem. As a quick side note, the two most frequently encountered errors here for beginning BHAV modders are:
- Undefined Transition: That means that execution has followed a path out of a function which is pointing to "Error", or to a node which does not exist.
- Too many iterations: The code is probably stuck in a loop. The game automatically forces an error on any object which gets stuck in a loop, because otherwise the game would hang.
The last line in this section is the number of iterations that the code has been running. Generally this isn't very helpful unless you're looking at very slowly performing loops.
The next section is a very simple summary of everything in the call stack at that moment in time. It is divided into frames, where each frame represents a function call. The frame at the top of the list is the one which was executing when the log was created.
The first two lines let you know what number is stored in the Stack Object ID variable, and which object that number represents. The next line, "Node", reports on which line of code failed. This number is in decimal, so you will need to change it to hexadecimal before you look it up in your BHAV editor.
The next line says which function was being run. The tree id is the decimal equivalent of the instance number of the function, and the name is, oddly enough, the name of the function.
The "from" line says what grouping of functions it comes from. If it is a local function, this will include the NREF name of the package that contains the BHAV. If it comes from a semiglobal or global library, it will name that library.
There's not a lot of information about the prim state at the moment, but it seems to be a way for primitives to report back when something goes wrong inside them. Normally when a primitive fails, it just follows the "false" execution path (unless there is a specific error message which can be associated with the problem) so this field makes it possible to tell which particular error occurred inside the primitive. Unfortunately information about the this field is fairly scarce, so this line isn't really very helpful.
The last line of each frame lists the values of each of the parameters and local variables. They are printed in order, starting from zero, with a space between each variable.
You'll notice that each of the lower frames is showing the state that the data was in when the next function was called - right down to the node (line) number of that particular function call. That's so that when the top frame finishes and returns either true or false, the next frame down can pick up right where it left off.
Underneath the stack trace is a list of all the globals, then dump of data stored in the stack owner (Me), and then a similar dump for the stack object. These potentially have subsections for person data, motives, contained objects (objects which are in its slots), object data, attributes, arrays, temps and inventory. Given what we've already covered above, these should be fairly self explanatory.
Below this section is a complete listing of all the objects in the currently active lot. You'll probably find a whole lot of objects you never realised you had in your lot, because they are invisible and non-interactable. That includes objects like careers, social plugins and meal options. Each object includes information about its object ID, its name and whether it is currently contained inside another object. This is useful if you need to look up an object ID stored in a local variable or a parameter.
Finally, there is a list of the game settings. It's pretty unusual for this to contain the solution to a BHAV problem, but it's handy to know that it's there.
The End?
So that's it. Well, not really - there's a heap more still to learn about the coding, but this information should make it a lot easier to figure out how objects do what they do!
If you have any questions about anything here, feel free to ask!
Overview
If you've never made a custom BHAV before, this is not the document for you. I'd suggest one of the Beginners' Tutorials (http://www.sims2wiki.info/SimAntics#Beginners.27_Tutorials) instead. The information here will probably only be helpful for those people who have been playing with BHAVs for a while, but who want to get a better understanding of the different ways in-game data can be stored and changed using BHAVs. It starts out with fairly basic stuff, but gets a little more complicated near the end.
At its simplest level, almost all programming is about manipulating data in some way or another, and SimAntics is no different. If you really want to create extraordinary hacks, it's important to understand the way the game stores this data, and when and how you should use it.
Everything written here is entirely based on observation of the game's behaviour, error logs, and general software architecture theory. If you find anything incorrect or unclear, please let me know asap and I'll try to correct it.
Enjoy, and good luck!
Literals, Variables and Constants
Before delving into the specifics of the Sims data model, it's important to have a very general grasp of data structures and their theory. If you have previous programming experience and can confidently say that you understand literals, variables and constants, please feel free to skip ahead.
Calling something a "literal value" is a fancy way of saying it's a regular, normal value. For example, the number 5 is a literal. The number 3.142 is also a literal. The phrase "the cat sat on the mat" is also a literal. This may seem a very odd idea, but it will make more sense shortly. At this point in time, just understand that the equation:
1 + 5
can be read a bit like this
literal(1) + literal(5)
which says "find the value of literal 1 plus literal 5".
For the purposes of my ongoing analogy, a literal is like a number that is written on a piece of paper in pen. Fair enough?
Now, variables are a bit different. Variables are names we give to a place where a literal can be stored. If you're imagining a literal as a value on a piece of paper, then imagine a variable as a plastic sleeve. You can put the literal into the plastic sleeve, you can take the literal out of the plastic sleeve, and you can put a different literal back into the plastic sleeve afterwards. But here's the clever bit - you can also put a label on that plastic sleeve. For example, you could label the plastic sleeve "Number of days until my birthday", and put a literal into it saying 50. If someone asks you "how many days until your birthday", then you can find the right plastic sleeve, then tell them what literal is stored in it. Tomorrow morning, you can take out the literal value you put in there, find the literal that is one lower, then put that into the sleeve instead. Now if someone asks you "how many days until your birthday", you can follow the exact same set of steps to find out, but the value you give them back will have changed.
In the Sims, variables don't always have such sensible names (although you can give them names if you wish). By default, they just have numbers. So if you have three variables, then you can access the values in them by number. For example:
variable(0) := literal(1)
variable(1) := literal(5)
variable(0) + variable(1)
is much the same as the equation listed under "literals" above. Note that the ":=" symbol means "gets the value of". The first line says "variable 0 gets the value of 1". The second line says "variable 1 gets the value of 5". The third line says "find the value of what's in variable 0 plus what's in variable 1".
All good? Good. If you're fine with that, then you should be okay with the rest of this document.
Finally, constants are special types of variables. You can only put a literal into them once. Then they can never be changed (ie - they can not "vary", they are "constant"). Instead of being like a plastic sleeve, this is more like encasing the literal in a layer of perspex. You can pick the value initially, but once it's in the perspex you can't change it again. For example, you might have a constant "how many days in January", which would hold the value 31. This shouldn't ever change, but putting it in a constant makes sure that you can always get at it when you need it.
So that's the three simplest ways of representing data that you will encounter when you're programming, and you'll find that most common programming language around today use them. So, if you want more information about how they work then documentation exists for it all over the Internet.
Now we get to the interesting bit - how this all works in the Sims.
Using built in variables
If you've done any BHAV tutorials before, you're probably familiar with the "Expression" command. This is a primitive command (ie - it's built into the game at the lowest level) which lets you manipulate variables directly. For example, you may know by now that if you enter:
Expression (My motive 0x0008 (Hygiene) := Literal 0x64)
in an interaction, then it will make the current sim's hygene value get set to maximum; 100 points (0x64 is hexadecimal for 100). But take a closer look at it given what you now know about literals and variables. Obviously, the second half is a literal, it is the literal value 0x64 or 100. But what about the left hand side? That has got to be some sort of variable it's storing it in. The variable is called "hygiene", and we're putting the literal value 100 into it.
Motives are one of the groups of variables that all sims get when they are created. Person data and Object Data are also groups of variables that all sims have. You can read the values in them, and you can write new values into them. Because they are variables, you can do all sorts of useful stuff. For example:
Expression (My motive 0x0008 (Hygiene) := My motive 0x000F (Fun))
will set the current sim's hygiene value to whatever their fun value is.
You can also use the Expression command with variables and constants to make decisions. For example:
Expression (My motive 0x0008 (Hygiene) > My motive 0x000F (Fun))
asks "is the value in My motive 0x0008 bigger than the value in My motive 0x000F?". If this is true, then the "true" execution path will be followed. If it is false, then the false path will be followed.
Objects also have similar groups of variables. The Object Data variables that exist in sims also exist in objects. However, the Person Data and Motives groups do not exist in regular objects (obviously). If you play around with the instruction wizard for Expression, you can see what sort of values you can manipulate.
Finally, there is a group of variables that cover information about the lot as a whole. These are called "globals". For example, there is a global variable for the time of day. They can be very handy to read, although you should be very careful when writing to them because they can affect a lot of different things.
Using constants
Constants in the sims are stored in BCON files. Most of the time, they are used to store values that get used in a lot different places in an object. The simple reason for this is that if you initially want the value 0x3 to be used in eight different BHAVs, then a few weeks later decide that 0x3 is too low and you want to change it to 0x5, it's a lot easier to change one number in a BCON file than it is to find all the places in the eight BHAVs where you used the number 0x3 and change them there.
Constant values get stored one to a line in a BCON file, but can be used in almost all the same places that literals can be used. You can specify which constant you're looking for using the instance number of the BCON file (eg - 0x1001) and the line number within the BCON file of the particular constant you want (eg - :0x00). To add constants to your project, you just have to add a BCON file to your package, and give it an instance number in the range 0x1000 to 0x1FFF that hasn't already been used by an existing BCON.
You can also name the constants in your BCON files if you want. Simply open your BCON and click the "Make Labels" button. This will create a new file in your package called a "Behaviour Constant Label". TRCNs are linked to BCONs by their instance number, so TRCN with instance 0x1000 holds the names of the constants defined in BCON 0x1000.
If you open this TRCN file, you can change the labels for each of the lines in your BCON.
Using local variables
Local variables, or "locals" are variables with a very short life span. They are created when a BHAV first starts, and as soon as the BHAV returns it is destroyed. However, you can use it to store and change values for as long as that BHAV runs.
This may seem fairly useless, but locals can be extremely useful. Use them to keep track of how many times you've gone through a loop, or to hold the object ID of a particular object so you can use it later in the function. They're really very handy!
If you want to use locals in your function, then you need to make sure they are created when the function is first called. You can do that by entering the number of local variables you will need into the "Local Var Count" box in the BHAV editor. Once that is done, you can use those variables in any way you wish within the function. It's important to remember that while the number in the "Local Var Count" box is the number of variables you will need, the identifying numbers for the variables start at zero. That is, if you say you need one local variable, it will be called local 0x0. If you say you need two local variables, you will get local 0x0 and local 0x1.
Passing variables to functions
A parameter is a value that you can send to a BHAV that you are calling, so that BHAV can use it like a local. For example, if you were writing a BHAV to give skill points to every sim on the lot, then you might have a parameter which said which skill type to give points to, and another to say how many skill points. That way, you could reuse that function for all sorts of different skills and values.
The function calling a BHAV can specify all of these values using parameters, and then the BHAV which is called will modify its behaviour based on them. These parameters are entered into the "operands" boxes. For any non-primitive functions, the order of the values follows one of two patterns.
The first option allows only literal values. If the parameters are being read this way, then two operand boxes represent one parameter. The first parameter goes in boxes one and two, the second parameter goes in boxes three and four, and so on. The only hitch is that the two boxes need to be read in reverse order. For example, if boxes one and two read 0x01 and 0x23 respectively, then the value of parameter one is 0x2301. (There is a very good reason for this which I won't go into now. If you're curious, ask some time!)
The second way to pass parameters is even more useful though, because it allows you send values from within variables. If the thirteenth operand box (fifth on the second row) is set to '1', this tells the game that instead of using two operand boxes per parameter, it should use three. In this case, the first of each set of three boxes is the "data owner", and the remaining two represent the data value. The "data owner" is the part that says what sort of data to use - literals, locals, constants, sim and object attributes, etc. The remaining two boxes specify the data value, that is, local *0x00* or, literal *0x05*. Like the first way of passing parameters, the two boxes need to be read in reverse order to get the actual value.
Using parameters in your own functions
Making your function use parameters is almost exactly like making your function use variables. You have to make sure that the function knows that it will have parameters, so you need to enter the number of parameters into the box labelled "Arg count". Once you have declared them in this box, you can use them anywhere in your code where you would use locals, but instead of selecting "Local 0x1" you would select "Param 0x1". Like locals, parameter labels start counting at 0x0.
Naming locals and parameters
In the same way that you can label each record in a BCON, you can also label each parameter and local you create for your functions. Simply open up the BHAV you wish to provide labels for, and click the "Make Labels" button. This will create a "Edith Simantics Behaviour Label" file, or "TPRP". You can then use this file to enter names for your locals and parameters, which should show up when you're reading through your code. This can be extremely useful if you have a few variables in your code - you're much less likely to forget what is going on!
Creating new attributes
If you've got a cloned or custom object, you can create new attributes for it, or change existing attributes. All of the attributes for an object are stored within the package, in one of two places. The first is in the OBJD file for the object's master tile. Under the "Attribute Number" field in the OBJD is a number representing how many attributes the object has.
The second place attributes are stored is in the text lists. Text list 0x100 contains one entry for each attribute the object has. If you add records to this text file in your package, you should get more attributes.
In most situations, the actual number of attributes in the object will be whichever is greater out of the OBJD record and the count of records in the text list. In some very rare circumstances though this may not be the case, so it's not a bad idea to make sure the values match across both locations.
Understanding the call stack
I'm afraid we're going to lapse into a bit more programming theory here, and for that I apologise. However, it's important to know a bit about how the game keeps track of what's running at any particular time, because a lot of other things are related to it.
The first thing to understand is what a "stack" is in computing terms. If you can picture a stack of open books, you're pretty much on the right track. A stack is a way of representing all the things that have happened, the order in which they happened, and what thing is currently happening.
For example, say you're reading a book, and you come across a word you don't know. You have to look that word up, but you don't want to lose your place in the book. So what you do is leave the book open on the right page, then grab a dictionary, put the dictionary on top of the open book, and look up the word. Once you've found the word, you can close the dictionary and put it away, the go back to reading the first book right from where you left off. The other thing you'll realise about this sort of stack is that you can only see what's in the top book of the stack. You can't see the text in the bottom book, because there are other books in the way.
A call stack is a lot like this, except instead of books it has functions that are being run. Every time a BHAV gets called, it gets put on the top of the call stack. The space on the stack that each BHAV takes up is called a "frame". The game can only see the frame (BHAV/book) on the very top of the stack, so it will only run code in the top BHAV. Consequently, it starts running that code from the beginning. Now, if that code makes a call to another BHAV, then the game will load a copy of the BHAV which is being called, and put that one on top of the stack. Because it's on the top of the stack now, the game will start executing that one until it either reaches another function call (in which case it will stack another BHAV on top and run that) or until it returns.
When you return from a function, you're saying "I'm finished with this BHAV", and so the game will take it off the top of the stack and get rid of it, revealing the BHAV which called it. The game will then keep going from wherever it left off. If your BHAV returned "true", then when the calling function starts up again it will follow the true path. If your BHAV returned "false", the calling function will follow the false path.
"Me" and the "Stack Object"
In the game, all objects (including Sims) have their own call stack. Any code that runs can tell which object's stack it is running in by looking at "Me" or "My". For example, "My 0x000B (Object ID)" is the variable holding the object ID of whichever object owns the stack the code is being run in. If you have an object which runs an "init" function, then that function will be run in its own object's stack. That's why "My" in an init function refers to the object itself. However, if you have code in an interaction, that code is run in the stack of the sim interacting with the object. That's why "My" in interactions refers to the sim. Because Me/My is always the owner of the stack, it's not something that you can change very easily in a function.
"Stack Object" behaves quite a bit differently though. The "Stack Object ID" is a variable that is created when every BHAV begins execution, and is local to that function. However, unlike most local variables, when the function starts this variable gets defaulted to the value that was in the "Stack Object ID" variable of the BHAV that called it. For example, if function "FirstFunction" does something like:
Stack Object ID := literal 0x5
Call SomeOtherFunction
Then when SomeOtherFunction starts, its stack object ID variable will be set to 5. This only works one way though! Once SomeOtherFunction starts, Stack Object ID behaves like a local. The function can change the Stack Object ID all it wants within its own code, but when it returns those changes will not appear in FirstFunction. FirstFunction will still have the value 0x5.
But why bother with Stack Object ID if it's really just a fancy version of a local variable?
Whenever an object is created in the sim game, it is assigned a number. This is called its object ID, and that number is unique to that object within any particular lot. Say you get a new sim on the lot. The game will automatically give it an object ID - let's say, 0x30. If you ran this code:
Stack Object ID := 30
Then the Stack Object would now be that sim. That means that if you looked at the variable:
Stack Object's motive 0x0008 (Hygiene)
you would get the Hygiene value of that sim. Handy, huh?
When you first enter an interaction, the stack object ID is set to the object that you clicked on to start the interaction. That means that if you don't change the stack object ID in the code, the stack object will point to the object the sim is interacting with.
A great many commands work based on what the stack object ID is. Others will store values into the stack object directly. For example:
0: Stack Object := 0 [True: 0x1, False: Error]
1: Set to Next (Object in room) [True: 0x2, False: Return True]
2: Go to Relative Position (Anywhere near, Stack Object) [True: 0x1, False: 0x1]
"Set to next" changes the value in Stack Object ID to the next highest value that meets the criteria that you provide, for example, objects in the same room as the sim. This piece of code will have sims walk to every object in their current room, one after the other.
A quick side note: an object's ID is only guaranteed to stay the same from the time you enter the lot until the time you leave it. While the numbers don't necessarily change between loads, they can do. Also, a Sim's object ID on one lot is not necessarily the same as it is on another lot. If you want to keep track of Sims uniquely, use their "Neighbour ID" number instead.
Using temps
Temps are stored at the stack level, like attributes, but they are only meant to be used to hold things temporarily.
Now that's not very useful if you're storing values just for use inside a function - it's generally better to use locals for that (although there are exceptions to this rule). Since they're only meant for temporary use, they're no good for holding values you want to keep for very long either. But they do have one important use - transferring values between different function calls.
If you use parameters then it is possible for a calling function to give values to the function it is calling, but that only works one way. The only way for a function to return a value back to its calling function is for it to store that value in a stack level variable, so that once the function has finished and returned to its calling function, the calling function can retrieve the value from the stack-level variable. This is the ideal use for temps.
As it happens, a lot of global functions use this technique to make coding complex behaviours easier. For example, if you call global 0x403 ("Get in Temp 0 - Aspiration") then the aspiration of the "me" sim will be stored in the temp 0x0 variable of your stack. Global 0x151 ("Get in Temp 3 - My Handedness") will store a different value in temp 0x3 depending on whether your sim is left or right handed. (As a side note to this, the "Animate Sim" primitive reads the value in temp 0x3 to determine whether an animation should be flipped. That means that if you call "Get in Temp 3 - My Handedness" then run an animate sim command straight after, then a left handed sim will use their left hand and a right handed sim their right!)
Reading and writing temps is much the same as attributes. Just select "temp" as the data owner, rather than "attribute"! Remember that they are really only good for temporary storage of data though, and any time you call a global, semi-global or primitive function it is possible that it might change the values without warning. You also mustn't assume that the value will stay the same after your function has finished. If you do use temps to communicate values across functions, and you want to keep those values or reuse them later, it's a very good idea to copy them into locals or attributes until you need them to be in the temps again.
How this relates to your error log
Once you've made sense of all of the above, then the error log will become your instant best friend when you're trying to edit BHAVS.
If you have a BHAV which is causing an error in game, then the game is able to create an error log telling you all sorts of information about what state the game was in when it happened. If you're not sure how to create an error log, check out this thread:
http://www.modthesims2.com/showthread.php?t=198229
The first (and probably most useful) section of an error log tells you what has actually gone wrong, and roughly where. The Object id row specifies which object owns the stack running the code with the error. That is, the id of the object treated as "Me" in the code. The "name" field directly following this holds the name of that object.
The stack size states the number of frames currently stored in the stack. Don't worry too much about this line, it's not very helpful for debugging.
The next line, "Error", will give you a hint as to what has actually gone wrong. There are a limited range of messages that can be reported, but most of the time it's still a good indication of what has caused the problem. As a quick side note, the two most frequently encountered errors here for beginning BHAV modders are:
- Undefined Transition: That means that execution has followed a path out of a function which is pointing to "Error", or to a node which does not exist.
- Too many iterations: The code is probably stuck in a loop. The game automatically forces an error on any object which gets stuck in a loop, because otherwise the game would hang.
The last line in this section is the number of iterations that the code has been running. Generally this isn't very helpful unless you're looking at very slowly performing loops.
The next section is a very simple summary of everything in the call stack at that moment in time. It is divided into frames, where each frame represents a function call. The frame at the top of the list is the one which was executing when the log was created.
The first two lines let you know what number is stored in the Stack Object ID variable, and which object that number represents. The next line, "Node", reports on which line of code failed. This number is in decimal, so you will need to change it to hexadecimal before you look it up in your BHAV editor.
The next line says which function was being run. The tree id is the decimal equivalent of the instance number of the function, and the name is, oddly enough, the name of the function.
The "from" line says what grouping of functions it comes from. If it is a local function, this will include the NREF name of the package that contains the BHAV. If it comes from a semiglobal or global library, it will name that library.
There's not a lot of information about the prim state at the moment, but it seems to be a way for primitives to report back when something goes wrong inside them. Normally when a primitive fails, it just follows the "false" execution path (unless there is a specific error message which can be associated with the problem) so this field makes it possible to tell which particular error occurred inside the primitive. Unfortunately information about the this field is fairly scarce, so this line isn't really very helpful.
The last line of each frame lists the values of each of the parameters and local variables. They are printed in order, starting from zero, with a space between each variable.
You'll notice that each of the lower frames is showing the state that the data was in when the next function was called - right down to the node (line) number of that particular function call. That's so that when the top frame finishes and returns either true or false, the next frame down can pick up right where it left off.
Underneath the stack trace is a list of all the globals, then dump of data stored in the stack owner (Me), and then a similar dump for the stack object. These potentially have subsections for person data, motives, contained objects (objects which are in its slots), object data, attributes, arrays, temps and inventory. Given what we've already covered above, these should be fairly self explanatory.
Below this section is a complete listing of all the objects in the currently active lot. You'll probably find a whole lot of objects you never realised you had in your lot, because they are invisible and non-interactable. That includes objects like careers, social plugins and meal options. Each object includes information about its object ID, its name and whether it is currently contained inside another object. This is useful if you need to look up an object ID stored in a local variable or a parameter.
Finally, there is a list of the game settings. It's pretty unusual for this to contain the solution to a BHAV problem, but it's handy to know that it's there.
The End?
So that's it. Well, not really - there's a heap more still to learn about the coding, but this information should make it a lot easier to figure out how objects do what they do!
If you have any questions about anything here, feel free to ask!