Replies: 5 (Who?), Viewed: 25253 times.
Original Poster
#1 Old 30th Jan 2016 at 8:46 PM
Default InventoryType Overrides via Scripting
I swore I wouldn't do any more really difficult modding, and then swore and swore and swore at myself all last night as I implemented this into the wee hours of the morning....

The ITUN, "objects.components.inventory_enums" has always been a source of mod conflict with mods that create new InventoryType enums and INVENTORY_TYPE_DATA tuning. Only one override can exist, and if it doesn't include the values from another mod it will eliminate their settings. What we need is a method of adding those values in a script, so there won't be conflicts.

Previous methods of updating tuning on the fly failed because the ITUN overrides are loaded BEFORE scripts are ever loaded. By the time we could see the values, they were set in stone and not modifiable. So we have to do some magic to actually modify them, and do that before the rest of the game tuning is loaded, so the inventory types are defined for the loaded tunings.

The following code does the trick. It needs to be run when the module is loaded so it happens prior to the other tunings are loaded by the game. It could be placed in a function, and then that function called by the module itself, but since we never ever want to run it again after the game is fully loaded it seemed okay, and maybe even preferable, to just run it outside of any functions.

This does not handle GAMEPLAY_MODIFIERS, as that isn't needed for my mods and I don't know of any mods that really override that stuff. The same tactics could work for it, but generating the tuning for it would be slightly more involved due to that tuning containing subtunings (like the "decay_modifiers" which is a list). If somebody really needs that, they can likely figure it out on their own - I'm still swearing at myself for doing this much.
import sims4.collections
from _sims4_collections import frozendict
from objects.components.inventory_enums import InventoryType, InventoryTypeTuning

# Remove any old inventory type that may exist if another mod overrides the InventoryType class
# via tuning to make their mod compatible with our old version.
# This try/except madness can be completely eliminated from a new mod that wasn't previously using
# an override for the InventoryType enum, as no other mods would have your values in their override
# to try and make their mod compatible with yours.  It doesn't hurt to leave it in, though!
    # This both tests for the enum entry and saves the old value so we can delete it from the InventoryType DynamicEnum
    # If it doesn't already exist, we drop down to the except below and continue.
    old_value = InventoryType.PACKING_CRATE
    # This should be true, so remove the tuning from the INVENTORY_TYPE_DATA
    if InventoryType.PACKING_CRATE in InventoryTypeTuning.INVENTORY_TYPE_DATA:
        # Since we don't know the internals of the frozendict class, we can't shortcut this
        # So we create a new dictionary with all the previous values EXCEPT the one we want to delete
        new_dict = {}
        for k,v in InventoryTypeTuning.INVENTORY_TYPE_DATA.items():
            if k is not InventoryType.PACKING_CRATE:
                new_dict[k] = v
        # And then update the INVENTORY_TYPE_DATA with the new frozendict
        InventoryTypeTuning.INVENTORY_TYPE_DATA = frozendict(new_dict)
    # We know the internals of the DynamicEnum, so we can remove the old inventory type by hand
    with InventoryType.make_mutable():
        delattr(InventoryType, 'PACKING_CRATE')
        del InventoryType.name_to_value['PACKING_CRATE']
        del InventoryType.value_to_name[old_value]
    # There was no old tuning, which is just fine

# Add the new InventoryType, using a 32-bit hash of the name for the id (high-bit set)
# *** We MUST NOT USE a 64-bit value here, it will seem to work ***
# *** here, but will throw ValueError exceptions later!         ***
with InventoryType.make_mutable():
    InventoryType._add_new_enum_value('PACKING_CRATE', 2862616782)

# Create our INVENTORY_TYPE_DATA tuning entry

# First, create an immutable_slots_class
immutable_slots_class = sims4.collections.make_immutable_slots_class(set(['max_inventory_size', 'put_away_allowed', 'shared_between_objects', 'skip_carry_pose_allowed']))
# Then create a new instance of the class with our tuning values
packing_crate_inventory_type_data = immutable_slots_class({'max_inventory_size':4294967295, 'put_away_allowed':False, 'shared_between_objects':False, 'skip_carry_pose_allowed':True})

# Finally, add our INVENTORY_TYPE_DATA tuning to the InventoryTypeTuning
InventoryTypeTuning.INVENTORY_TYPE_DATA = InventoryTypeTuning.INVENTORY_TYPE_DATA + {InventoryType.PACKING_CRATE:packing_crate_inventory_type_data}

I will be adding this to my packing crate mod to eliminate conflicts with other inventory enum overrides someday, though not today. If anything is not clear, feel free to ask questions or you can wait until packing crates is updated and that will give a working example.

This was quite thouroughly tested, works for my packing crates, and throws no exceptions - but if anyone sees any reasons why something being done above is bad bad bad, feel free to yell out!
Original Poster
#2 Old 30th Jan 2016 at 8:59 PM
One additional note, the InventoryType internals have been pretty (if not completely) stable since the game's initial launch. So this method should be fairly "future proof" as well.
Field Researcher
#3 Old 14th Mar 2016 at 2:37 AM
Hi, I finally got this working (my first ever attempt at python so it probably took more figuring out than it should have) but now I need to add my new inventory type to all the types of objects I want to be able to put in my new inventory type. I've been reading over the injector thread and found it somewhat helpful but I still don't know have to inject inventory types. I opted to put my question in this thread since the code I am trying to work with is the code you posted here. I have nearly no knowledge of python just what I gained while figuring out how to get the code you posted above to work. I have a custom tuning file I made to make objects hold my new inventory type and it works other than the fact that there are no valid objects to put in it. I pretty much want it to hold all of my bait while my sim is not actively fishing since he is also the family cook and gardener and I don't like the storage chests that came with the spooky stuff update.

I have moved again this time to a private domain here are the links:
Sims 4:
Sims 2:
Original Poster
#4 Old 18th Mar 2016 at 4:44 PM
Originally Posted by MonoChaos
I need to add my new inventory type to all the types of objects I want to be able to put in my new inventory type.

Only got a quick minute before going to work, but you can probably figure this out from looking at the script for the Packing Crate mod. In the packing_crate_load_data_into_class_instances() function I create a new tuning for the inventory_item for objects and assign it to objects. I'm also adding some super affordances and doing this with all objects not just a set of them, so you can't really use that code as is, but the key things are creating the inventory_item_tuning using the TunableFactory, and then adding that to the components of the object using the clone_with_overrides. I can expand on this later if you run into problems.
Lab Assistant
#5 Old 15th Nov 2016 at 2:22 AM
I really need a working example of this...
Original Poster
#6 Old 8th Nov 2017 at 4:33 AM
In updating the Packing Crates mod I discovered that a few changes were required to this process for the latest game versions. The first, no biggie, is that the imports needed updating as some of the definitions have moved about.

The bigger change is the need to import and use the ObjectShareability enum, which is now used for the value of the shared_between_objects tuning of the INVENTORY_TYPE_DATA instead of just True/False as it was back when this was first written.

See the script in the Nov 7, 2017 update for Packing Crates if you are interested in seeing how this stuff works in a real mod.
Back to top