Adventures In Animations
Notes on using the Reach/Put primitive to pick up and put down objects.
As part of my Social Bug Hunting mod, I needed a way to keep duplicate bugs for giving as gifts and/or trading. Having considered "tokens" (hidden objects in inventory), I decided to use a "real" object, as that would a) allow Sims to interact with it directly if desired, and b) provide an object for adding debug options to. The obvious item to use was the bug collecting jar. While making the mod I hit several stumbling blocks and this series of notes/tutorials is the condensed discoveries - I've created a simplified object so it's easier to follow just the relevant code.
Part 1 - Cloning the Bug Jar and parring it down
The following are my notes I made while cloning the Bug Jar and stripping out all unwanted features. You may want to replicate this process, or just start with the object at the end of part 2.
- Start SimPE
- Open the "Object Workshop"
- Click "Start"
- Expand "Unknown" and scroll down to select "Bug Jar"
- Click "Next"
- Ensure task is "Clone" and click "Set Defaults"
- Click "Next"
- Change the title, set a price of 1, add a description (if desired)
- Click "Finish"
- As we'll be using the standard mesh and textures, just click "OK" when the "Scenegraph rename Wizard" pops up
- Save the package into your downloads testing folder
- Switch to "Plugin View"
- Open the OBJD resource
- Change the Filename, allocate a new GUID, set the "Orig. GUID" to be the same and set the "Fallback GUID" to 0x00000000
- Click "Commit"
- Click the "Catalogue Sort" tab
- Check only "Misc." in the "Room Sort" area and "General" in the "Function Sort" area, and in the "Overall Sort" drop-down select "General / Miscellaneous"
- Click "Commit"
- Click the "RAW Data" tab
- Scroll down to section 7 and change "0x0014: slots id" to 0x0000
- Click "Commit"
- Open the STR# instance 0x0085 and delete entry 0x01 (this will leave two entries - 0x00 blank and 0x01 as "jarButterFlies")
- Delete the STR# 0x0087, 0x0088, 0x008F, 0x0090, 0x0094, 0x0100, 0x012E and 0x012F resources
- Delete the SLOT 0x0080 resource
- Delete the NREF 0x41A7 resource
- Open the OBJf resource and remove the action BHAVs for "load", "user placement", "user pickup" and "Start Live Mode"
- Click "Commit File"
- Open the TTAs resource and delete entries 0x01 and 0x02 (this will leave a single entry - 0x00 as "View")
- Click "Default lang only", then "Commit File"
- Open the TTAB resource and delete the entries 0x01 and 0x02 (this will leave a single entry - 0x00 as "View")
- Click "Commit File"
- Delete the BHAV 0x1004, 0x1005, 0x1006, 0x007, 0x1008, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, 0x1010, 0x1011, 0x1012, 0x1013, 0x1014 and 0x1015 resources
- In BHAV 0x1000 "Function - Init", delete (in this order) lines 0x0B, 0x0A, 0x06, 0x05, 0x02 and 0x01, then click "Inge's InitLinker"
- Click "Commit File"
- In BHAV 0x1001 "Function - Main", select line 0x05, then click "Delete to end", now delete lines 0x03, 0x02, 0x01 and 0x00, finally set the "True Target" for (the only) line 0x00 to be 0x00
- Click "Commit File"
- In BHAV 0x1003 "Interaction - View TEST", select line 0x01, then click "Delete to end", now set the "False Target" for (the only) line 0x00 to be "Return True"
- Click "Commit File"
- In BHAV 0x1002 "Interaction - View" click "Sort"
- Select line 0x05 and change the "True Target" to 0x08, click "Sort" again
- Select line 0x0B and change BOTH "True Target" and "False Target" to 0x0E, click "Sort" again
- Select line 0x10 and change the "True Target" to 0x06
- Select line 0x11 and click "Delete to end"
- Click "Commit File"
- Delete the BCON 0x1002 and 0x1003 resources
- Save the package
- Exit SimPE
- Start Sims2
- Enter your test hood and test family
- Place the jar clone (it will be found in Buy Mode, Misc, Misc at $1) on a surface
- Confirm that "View" still works!
Download: Cloned Bug Jar
Part 2 - Tidying up the cloned Bug Jar
With a working parred down Bug Jar, I then rearranged/renumbered the BHAVs, stripped out some more code, removed unneeded BCONs and added labels
Download: Tidy Bug Jar
Part 3 - Pick Up, Inspect and Put Down the Bug Jar
As part of the "Add To Collection" option (where one Sim can leave their "swapsies" jar on a surface for another (autonomous) Sim to take needed bugs from) I wanted the Sim not to just approach and view the jar, but to pick it up, inspect it's contents and then put it back down again.
Getting a Sim to pick the jar up was quite straight forward, however getting them to then put the jar back onto the original surface, and getting the jar to stand upright, was a whole different proposition.
Part 3a - Pick Up, Inspect and Put Down ON A SURFACE
As can be seen from the BHAV "Interaction - View", the standard sequence of actions when interacting with an object is to
- Approach
- Call the global "Standard Entry"
- Do something
- Call the global "Standard Exit"
If you look at the "Standard Entry" BHAV, it looks quite intimidating ... "Animate Sim" ... Relationship" ... object's dirty state ... my neatness ... "Coin Flip" ... "Effect Stop/Start". Now take a closer look at line 0x02 - nothing leads to it (as indicated by the black incoming arrow at the right). Select line 2 and click "Delete", the new line 2 is also unreachable, delete that and delete the new line 2 (old line 4) as that will also be unreachable. Still quite intimidating but look at line 0x01 - that's now unreachable! Repeat until there are no incoming black arrows ... "Standard Entry" is actually only two lines of code! The first sets the "In Use" flag for the object and the second "zeros out" a relationship between the object and itself - no idea why it does this
The "Standard Exit" BHAV clears the "In Use" flag, adds a relationship between the object and itself that references the Sim's NID, and sets the object's "lockout count" (no idea what the relationship is for, but the latter is used to stop a Sim trying to repeatedly use the same object)
What IS important is that if your code calls "Standard Entry" it MUST call "Standard Exit"
So out BHAV "Interaction - Inspect" is going to be
- Approach
- Call the global "Standard Entry"
- Pick up object (return surface in T0)
- Do something
- Put down object (using preferred surface in T0)
- Call the global "Standard Exit"
As both picking the object up and putting it down again can fail, we need two ways out, hence the two calls to "Standard Exit" which permits us to use different lockout counts if desired
So how does a Sim pick an object up? The Sim needs to approach it, but not in exactly the same way as if they were viewing it, so we'll adapt out BHAV "Sub - Approach" to take a parameter to indicate if the Sim is going to view the object or reach for it.
- Line 0x00 is a "paranoia check", is the target object still on the lot
- Line 0x01 checks the parameter to see if we're "viewing" or "reaching for" the object
- Lines 0x02 and 0x03 approach the object to view it
- Line 0x04 approaches the object to reach for it
- Line 0x05 checks that once we've reached the object, it's still available for use (as another Sim may have beaten us to it!)
Once we've made it to the object (and it's still available for use) we can pick it up via BHAV "Sub - Pick Up", for the moment we're assuming the object is on a surface, we'll deal with objects on the floor in the Part 3b
- Line 0x00 is a "paranoia check", to see if the Sim is already holding the object
- Line 0x01 checks that the object is on a surface
- Line 0x02 sets the Stack Object to be the surface the target object is on
- Line 0x03 is "magic" ... it directs the Sim to pick up whatever object is on the surface, but how?
The "Run Functional Tree" primitive (0x0014 - https://modthesims.info/wiki.php?title=0x0014) looks in the OBJf resource for the current Stack Object (which we set to the surface the target object is on) and executes whatever BHAV is given in the "pickup from slot" entry. If we look at the OBJf resource for a surface type object, eg "Table - End - Luxury", we'll see that the "pickup from slot" guardian BHAV makes sure there is a suitable object on the surface and the action BHAV executes (via the Run Tree by Name primitive) the "CT - Reach - Grab" BHAV from the target object.
So the "magic" happens in our BHAV "CT - Reach - Grab"
- Line 0x00 calls a global BHAV to find out if the Sim is right or left handed
- Line 0x01 uses the "Reach/Put" primitive (0x0074 - https://modthesims.info/wiki.php?title=0x0074) to pick up the object
- Line 0x02 returns true or false depending on whether or not the Sim is currently holding anything - Maxis code doesn't seem to trust the return from the "Reach/Put" primitive to ascertain this)
The "Reach/Put" primitive wants two animations, one for the Sim's pick-up action and one to position the object once it has been picked up
Our "do something" is the BHAV "Sub - Do Inspect" and this displays a notification and pauses for a few Sim minutes.
Putting the object back down is slightly more complicated than picking it up. While the Sim is inspecting the object the player may cancel the action from their queue, in which case we need to decide if we want the Sim to end the action as quickly as possible, dropping the object on the floor, or if we want the Sim to replace the object from whence it came. As the Sim is by the surface the object is on, it would take longer to drop the item on the floor - so we want the Sim to replace the object.
- Line 0x00 makes the Sim "non-interruptible" - even if the player cancels the action, the BHAV "Sub - Put Down" will perform all of its animations and execute code from the "Run Functional Tree" primitive (and, I'm guessing, also from the "Run Tree by Name" primitive). Note, if you make the Sim non-interruptible it is your responsibility to make sure to reset the value to zero!
- Line 0x01 is redundant (I forgot to remove it!)
- Lines 0x02 to 0x04 check that the object passed as the preferred surface to put the item onto is valid, and if not uses the Sim as the starting point for a suitable place to put the item. Unfortunately, I deleted the line of code that uses this information!
- Line 0x05 is more "magic" - but we know how to decode such "magic", and this is how the BHAV "CT - Reach - Drop" gets executed
- Lines 0x06 and 0x07 make the Sim "interruptible" again - one for a True exit and one for a False exit
The actual code to place the item on the surface is in BHAV "CT - Reach - Grab"
- Line 0x00 calls a global BHAV to find out if the Sim is right- or left-handed
- Line 0x01 stops the Sims "carry" animations. If this is omitted the Sim's arm will remain bent after the item is replaced
- Line 0x02 stops the animation the item is doing. Seems to be unnecessary, but if omitted the later animation to place the jar up-right will fail
- Line 0x03 verifies that the surface we're trying to item onto still exists - it's not impossible that the player paused the game and deleted it!
- Line 0x04 sets the target slot in the surface object that we're going to place the item into
- Line 0x05 uses the "Reach/Put" primitive to actually place the item onto the surface
- Line 0x06 is the fall back to put the item on the floor if the surface no longer exists
Look closely at the SimPE descriptions of the primitives in lines 0x05 and 0x06 - they appear to be the wrong way round! But the code works; also, this code was taken directly from a Maxis object. In fact, I checked numerous Maxis "CT - Reach - Drop" BHAVs and they are ALL coded like this, so I can only assume that a) the SimWiki is incorrect and (consequently) b) SimPE is also incorrect.
Download: Inspect From Surface (expect weird behaviour if you place the jar on the floor and then inspect it, or you remove/fill all surfaces after the jar has been picked up - we will address these issues in the next part)
Part 3b - Pick Up, Inspect and Put Down from a surface OR THE FLOOR
So what happens if the jar is placed on the floor, or there are no available surfaces to put the jar back onto? This part will deal with those situations.
We'll start by splitting out the two lines of code to pick an item up from a surface into its own BHAV "Sub - Pick Up From Surface"
And changing the BHAV "Sub - Pick Up" to pick up from the floor if the jar is not on a surface
And we'll write a new BHAV "Sub - Pick Up From Floor" to do the work
Note that this code is identical to BHAV "CT - Reach - Grab". While we could have used that BHAV it's not a good idea as any additions to "CT - Reach - Grab" for picking up from a surface will also affect picking up from the floor, and this "connection" would not be apparent from the BHAV's name. We could use the "Run Tree by Name" primitive to call "CT - Reach - Grab" from "Sub - Pick Up From Floor" but a) this needs an extra STR# resource and b) is again not obvious that a change to "CT - Reach - Grab" will also affect "Sub - Pick Up From Floor". If the code had been more than a few lines I would have created another BHAV "Sub - Pick Up Generic" with the shared code and called that from BOTH the "CT - Reach Grab" and "Sub - Pick Up From Floor" BHAVs.
Similarly, I've moved the code to put an object onto a surface into its own BHAV "Sub - Put Down On Surface"
- Line 0x05 is the previously omitted code to find the surface nearest to the given object (in Local 0) - either the surface the item was picked up from (passed in Param 0) or the Sim (if the preferred surface no longer exists or was the floor)
And changing the BHAV "Sub - Put Down" to put on the floor if the attempt to place the jar on a surface fails
And we'll write a new BHAV "Sub - Put Down On Floor" to do the work
- Lines 0x00 to 0x03 set the grasp/carry animations
- Line 0x04 finds an empty floor tile and walks the Sim to it. Note that this may fail if there are no reachable empty floor tiles - we'll deal with that scenario in Part 3c
- Line 0x05 and 0x06 place the object onto the floor
- Line 0x06 stops the carry animation, which un-bends the Sim's arm
Download: Inspect From Surface or Floor
Part 3c - Pick Up, Inspect and Put Down allowing for failures
It is possible that we can't find anywhere to put an object down - all the surfaces may have been filled (plates from Sims that have finished eating) and open floor tiles may be blocked (by other Sims standing in the way). If this happens, we can attempt to teleport the object to an available space, but if that fails, our only option is to delete the object (otherwise it would be permanently stuck in the Sim's hand)
- Line 0x03 attempts to teleport the object to an available space
- Line 0x04 uses the global BHAV "Fade Object - OUT" to fade the object and then delete it
- Line 0x05 returns the Sim's arm to normal
(Aside: there is an alternative to deleting the jar, and that's to put it into the Sim's inventory - I'll leave this as an exercise for the reader after Part 4!)
Download: Inspect Bug Jar (Complete)
Part 4 - Placing the Bug Jar into the Sim's inventory
I dislike having to interrupt the flow of my Sims to open an inventory and place an item into it. I much prefer to add a "Put Away" option to the item to achieve this. For some items I make this an immediate action and just teleport the item into the inventory. However, for the Bug Jar I wanted the Sim to walk up to the jar, pick it up and then place it into their inventory.
The BHAV "Interaction - Put Away" performs the necessary actions
- Lines 0x00 to 0x03 should be familiar by now - if not, I suggest you go back and re-read the previous sections
- Line 0x04 I'll return to in a minute
- Line 0x05 uses the global BHAV "Inventory - Visible - Add Item" to put the jar into the Sim's inventory, if that fails ...
- Line 0x06 ... deletes the jar
- Line 0x07 returns the Sim's arm to normal
So why is that call to "Standard Exit" where it is and not after the "Animate Stop" line. If we call "Standard Exit" at the end of the BHAV, the object we place into the Sims inventory will still be marked as "in use", so when it's removed from inventory it will be unusable. So we need to call "Standard Exit" before placing the item into inventory.
(Aside: there is an alternative to deleting the jar, and that's to put it back down. I'll leave this as an exercise for the reader but take care! If you've also decided to add the item to inventory rather than deleting it if the Sim fails to put it down make sure you don't end up in an infinite loop "put down, fail place in inventory, fail put down, fail place in inventory, ...)
We have a problem, and it doesn't manifest itself until we take the jar out of the Sim's inventory - the jar is on its side!
I tried many ways to stand the jar up before placing it into inventory, none of which worked. While digging around in the standard magazine objects I came across the solution. When an item is placed into inventory it is, in computer parlance, "serialized" which is exactly the same process used when the game is saved. So recreating an object as it is taken out of inventory is the same process as when a lot is loaded, and in both situations the OBJf "load" function is called to handle any special processing required.
So, what we need is a BHAV "Function - Load" hooked up to the "load" OBJf entry
- Line 0x00 checks to see if the container of the jar is a Sim, ie the jar is in a Sim's hand, and if so we do nothing, otherwise ...
- Line 0x01 uses the "o-generic-resetToZero-pose" animation to stand the jar upright (its default position)
Download: Putting the Bug Jar Away
Part 5 - Throwing out the Bug Jar
Newspapers, magazines, empty bags, etc can be disposed of in the trash. We're going to add the same functionality to the jar
The BHAV "Interaction - Dispose" performs the necessary actions
- Lines 0x00 to 0x03 should be familiar by now - if not, I suggest you go back and re-read the previous sections
- Line 0x04 uses the global BHAV "Dispose Trash In Hand" to walk the Sim to the nearest bin and throw the jar out, if that fails ...
- Line 0x05 ... deletes the jar
As this is the third time I've copy-pasted the lines to delete the jar, I placed them into their own BHAV "Sub - Delete From Hand"
Download: Throwing Out the Bug Jar
Hope these notes are of use to someone. Enjoy!