Posts: 2,671
Thanks: 63070 in 192 Posts
25 Achievements
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.
Code:
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!
try:
# 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]
except:
# There was no old tuning, which is just fine
pass
# 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!