- Site Map >
- Modding and Creation >
- Sims 4 Creation >
- Modding Discussion >
- Adding Interactions to Sim/Object Super Affordance List via Python
- Site Map >
- Modding and Creation >
- Sims 4 Creation >
- Modding Discussion >
- Adding Interactions to Sim/Object Super Affordance List via Python
Posts: 2,671
Thanks: 62741 in 190 Posts
Is there a way to generate basic cheat command executing interactions (CommandSuperInteraction) purely from code? There are nice tutorials for Sims 3.... I got Python code to run nicely so if I could just do it from there.. |
I've written a bit of a tutorial that demonstrates how to build all the XML, SimData and attach the super affordances to an object. It's actually kind of a useful mod, too, I think I'll keep it in my game!
Attached is a zip file containing a PDF tutorial and all the example files. The mod adds an Enable and Disable Testing Cheats pie menu to the lot mailboxes.
EDIT 07/05/2018 - Fogity's Modding Toolbox as used in the tutorial is no longer available, so I have written an updated version available here in the Modding Tools forum.
Updated 11/26/2017 for latest game patch:
- Changed interactions to ImmediateSuperInteraction class.
- Updated tutorial PDF for S4PE changes to STBL edit process.
- Updated script to use the game's cheat service.
Tutorial - Mailbox Testing Cheats.zip (248.4 KB, 2956 downloads) - View custom content | ||||||||||
0 2015-03-22 14:39 Tutorial - Mailbox Testing Cheats/ 2110 2017-11-26 16:40 Tutorial - Mailbox Testing Cheats/MailboxTestingCheats.package 309 2015-03-22 02:14 Tutorial - Mailbox Testing Cheats/S4_03E9D964_00000000_88CDD71DF4862017.xml 99 2015-03-22 02:30 Tutorial - Mailbox Testing Cheats/S4_220557DA_00000000_00918772618D89D4.stbl 413 2015-03-22 02:17 Tutorial - Mailbox Testing Cheats/S4_545AC67A_00E9D967_88CDD71DF4862017.data 5385 2017-11-26 15:57 Tutorial - Mailbox Testing Cheats/S4_B61DE6B4_00000000_0000000000003A5C.xml 1749 2017-11-26 15:47 Tutorial - Mailbox Testing Cheats/S4_E882D22F_00000000_9C65A60650DB3221.xml 1776 2017-11-26 15:47 Tutorial - Mailbox Testing Cheats/S4_E882D22F_00000000_E11EE2714E206770.xml 0 2015-03-22 14:34 Tutorial - Mailbox Testing Cheats/Scripts/ 889 2014-09-29 20:25 Tutorial - Mailbox Testing Cheats/Scripts/injector.py 1474 2017-11-26 16:57 Tutorial - Mailbox Testing Cheats/Scripts/mailbox_testing_cheats.py 274155 2017-11-26 16:38 Tutorial - Mailbox Testing Cheats/Tutorial - Mailbox Testing Cheats.pdf --------- ------- 288359 12 files |
Posts: 18
I assume you are referring to the Fogity toolbox here: http://modthesims.info/showthread.p...25#startcomment - however, the link just leads to a non-viewable forum thread, so it's apparently not obtainable these days.
Posts: 2,671
Thanks: 62741 in 190 Posts
I assume you are referring to the Fogity toolbox here: http://modthesims.info/showthread.p...25#startcomment - however, the link just leads to a non-viewable forum thread, so it's apparently not obtainable these days. |
Hmm, well you can create hashes in s4pe, but Fogity's toolbox is by far my favorite tool for generating hashes as it has the built in hex/dec converter as well. @Fogity any plans to make this available again, or should I be suggesting folks just use s4pe for hashing?
Posts: 47
Thanks: 1429 in 46 Posts
I wanted to try your example, but since I can't find the modding toolbox anywhere I haven't really gotten anywhere with it. I assume you are referring to the Fogity toolbox here: http://modthesims.info/showthread.p...25#startcomment - however, the link just leads to a non-viewable forum thread, so it's apparently not obtainable these days. |
Hmm, well you can create hashes in s4pe, but Fogity's toolbox is by far my favorite tool for generating hashes as it has the built in hex/dec converter as well. @Fogity any plans to make this available again, or should I be suggesting folks just use s4pe for hashing? |
I made a quick CLI tool you could use in the meantime until Fogity gets it re-uploaded. Just double click on Hasher.bat and enter in the text you want to hash. It's obviously not as pretty, but it'll get the job done.
Edit: Oh, and you may want to look up how to copy values from cmd.exe if you haven't already. You'll need to enable QuickEdit, but there are instructions for how to do this online. You'll also need Python installed and added to your path (it gives you an option to do that during installation).
hasher.zip (788 Bytes, 169 downloads) - View custom content | ||
1238 04-09-2015 21:38 hash.py 84 04-12-2015 13:15 Hasher.bat --------- ------- 1322 2 files |
Posts: 125
Hmm, well you can create hashes in s4pe, but Fogity's toolbox is by far my favorite tool for generating hashes as it has the built in hex/dec converter as well. @Fogity any plans to make this available again, or should I be suggesting folks just use s4pe for hashing? |
I am working on a new version, but I am not quite done with it yet. So in the meantime I can give you a link to the old version (they are functionally equivalent), and feel free to share it if someone else wants to use it.
Here are the links: Mac OS X and Windows
Posts: 11,006
Thanks: 423049 in 1121 Posts
Stuff for TS2 · TS3 · TS4 | Please do not PM me with technical questions – we have Create forums for that.
In the kingdom of the blind, do as the Romans do.
Posts: 125
@Fogity, I added those links to the Tools list (sticky) in Tools -- it would be cool if you could post a link in that that thread when you have a new official version (or versions) available, so I can remove those interim links. Thanks! =) |
I'll do that.
Posts: 6,939
Thanks: 7556 in 405 Posts
The injection works like this:
sa_instance_ids = (17608706005782878675, 13258313599595875556, ...etc for all the interactions you're adding) object_ids = (14845, 34680, 40340, 34682, 34684, 34679, 34678, 36369, 36370, 77507, ...blah, blah, whatever objects you want to attach to, which could also be a "Sim" object) @inject_to(sims4.tuning.instance_manager.InstanceManager, 'load_data_into_class_instances') def inject_load_data_into_class_instances(original, self): try: original(self) for (key, cls) in self._tuned_classes.items(): if hasattr(cls, 'guid64') and cls.guid64 in object_ids: add_super_affordances_to_object(object_tuning) except: _write_error_log(traceback.format_exc()) pass
The add_super_affordances_to_objects method looks very similar to what we already have in the tutorial for adding testing cheats to mailboxes:
def add_super_affordance_to_object(object_tuning): affordance_manager = services.affordance_manager() sa_list = [] for sa_id in sa_instance_ids: tuning_class = affordance_manager.get(sa_id) if tuning_class is not None: sa_list.append(tuning_class) if len(sa_list) > 0: object_tuning._super_affordances = object_tuning._super_affordances + tuple(sa_list)
Posts: 11,006
Thanks: 423049 in 1121 Posts
Stuff for TS2 · TS3 · TS4 | Please do not PM me with technical questions – we have Create forums for that.
In the kingdom of the blind, do as the Romans do.
Posts: 92
Thanks: 3803 in 5 Posts
Can you explain more about injecting into sims?
I have some experience with C++ but I don't have much experience with Python, only helped a guy long time ago in a brief part of his code. I also don't know anything about the attributes in the Sims 4 variables.
Posts: 6,939
Thanks: 7556 in 405 Posts
@Deaderpool Can you explain more about injecting into sims? I have some experience with C++ but I don't have much experience with Python, only helped a guy long time ago in a brief part of his code. I also don't know anything about the attributes in the Sims 4 variables. |
I can try to explain it a little. Scumbumbo wrote up a post describing it way way back, I think. I believe it was around the time Sims 4 was released. That was where I started reading about it and learning it. Also, looking through the py files in the script mods that he released usually had some small examples that I could "absorb" from.
In theory, any method in an EA python module, we can inject into and basically wrap it so our own method is called instead. From our method, we can then choose to call the base method when we want to (doing custom coding before or after it) or bypass it altogether and use our own script (I try to avoid this as you never know when they'll add something to the method that you aren't accounting for).
This was the basic injection wrapper functions that I used to get started from some of Scumbumbo's work:
from functools import wraps # method calling injection def inject(target_function, new_function): @wraps(target_function) def _inject(*args, **kwargs): return new_function(target_function, *args, **kwargs) return _inject # decarator injection. def inject_to(target_object, target_function_name): def _inject_to(new_function): target_function = getattr(target_object, target_function_name) setattr(target_object, target_function_name, inject(target_function, new_function)) return new_function return _inject_to
The basic concept is that you use an inject_to decorator on the method that you want to overwrite EA code giving it the details of what method you want to ovewrite. Using a very common example of wanting to add some code whenever you zone-into a lot with your Sim household active, you could do that within the Zone class in the "on_loading_screen_animation_finished" method. So, first you have to import the module that contains the "Zone" class, which is simply "zone". Then you have to write a method and put the inject_to decorator on it. Like so:
import zone @inject_to(zone.Zone, 'on_loading_screen_animation_finished') def inject_zone_loading(original, self): original(self) # Do any custom coding you want here, which will happen after the EA-scripts for zone loading finish.
You'll notice that the method I wrote has "original" and "self" as the parms. This comes first of all by looking at the method you're overwriting in the EA scripts. Looking up the zone.py script file and finding that method within the Zone class, you'd see this for the method's definition:
def on_loading_screen_animation_finished(self):
This is a really simple example of a method and I LOVE it when they are this simple. Any time you do a wrapper on a standard method, there is ALWAYS an "original" object that you'll need as the first parameter in the method you're writing to wrap around it. The "original" object is an actual reference to the EA function. So, to call that function and let the standard code run within your method, you would use original() sending it any additional parms defined by the core method, which in the example I showed above, is only "self". The call to "original(self)" would run that core method from within your own method.
As another example, let's say that EA has a class named "FooFighter" and it was in the foo_fighter.py module, and it had a method we wanted to inject into named "do_really_cool_stuff" that looked like this in the core script:
def do_really_cool_stuff(self, more_stuff, and_yet_more_stuff, optional_parm=True, so_many_parms=True)
Then your injection would HAVE to take the parms past "self" into account as well in your injection.
import foo_fighter @inject_to(foo_fighter.FooFighter, 'do_really_cool_stuff') def my_even_more_really_cool_stuff(original, self, more_stuff, and_yet_more_stuff, **kwargs): # Do any custom coding you want here, which will happen before the EA-scripts for do_really_cool_stuff runs. original(self, more_stuff, and_yet_more_stuff, **kwargs)
You can see that all the required parameters were specified on my new method in addition to the "original" object, and for all the optional parameters (anything with "="), I just used a generic "**kwargs" to catch them all and pass them to the original function. If I needed to overwrite those with some values or read what is in them, you can break them out within your method and the call to original just like we did with "more_stuff", "and_yet_more_stuff", etc.
That's what I would consider entry-level injection. It gets a lot more complicated as you get into @classmethod decorators or @flexmethod decorators. Those don't behave like standard methods so injecting into them has tricks that you have to do, and I don't know that I've even figured out exactly how to make @flexmethods work yet. I think I've worked around them and used different methods each time.
Keep in mind that if EA changes the parameters on the core method you're wrapping, you'll have to change your scripts as well to include any changes they've made. And, there are quite a few examples out there now of python script mods that can be learned from. I know that's what I did (and still do!)
Posts: 6,939
Thanks: 7556 in 405 Posts
Posts: 77
Thanks: 2941 in 11 Posts
import services import injector PTOEduc_sa_instance_ids = (10267729682623062937, ) @injector.inject_to(sims.sim.Sim, 'on_add') def PTOEduc_add_phone_affordances(original, self): original(self) sa_list = [] affordance_manager = services.affordance_manager() for sa_id in PTOEduc_sa_instance_ids: tuning_class = affordance_manager.get(sa_id) if not tuning_class is None: sa_list.append(tuning_class) self._phone_affordances = self._phone_affordances + tuple(sa_list)
But in game, it doesn't work, no new interaction is showing up... Is something missing ?
Posts: 92
Thanks: 3803 in 5 Posts
But in game, it doesn't work, no new interaction is showing up... Is something missing ? |
Yes.
import sims.sim
Posts: 77
Thanks: 2941 in 11 Posts
Yes.
Code:
import sims.sim |
̶
A̶c̶t̶u̶a̶l̶l̶y̶,̶ ̶I̶ ̶t̶h̶i̶n̶k̶ ̶I̶'̶v̶e̶ ̶m̶e̶s̶s̶e̶d̶ ̶u̶p̶ ̶w̶i̶t̶h̶ ̶t̶h̶e̶ ̶"̶i̶m̶p̶o̶r̶t̶ ̶m̶o̶d̶u̶l̶e̶"̶ ̶t̶h̶i̶n̶g̶ ̶i̶n̶ ̶P̶y̶t̶h̶o̶n̶ ̶:̶f̶a̶c̶e̶s̶l̶a̶p̶:̶ ̶,̶ ̶s̶o̶ ̶I̶'̶l̶l̶ ̶h̶a̶v̶e̶ ̶t̶o̶ ̶s̶e̶e̶ ̶t̶h̶a̶t̶ ̶l̶a̶t̶e̶r̶.̶.̶.̶ ̶B̶u̶t̶ ̶T̶h̶a̶n̶k̶ ̶y̶o̶u̶ ̶t̶o̶ ̶t̶e̶l̶l̶i̶n̶g̶ ̶m̶e̶ ̶w̶h̶a̶t̶ ̶w̶a̶s̶ ̶m̶i̶s̶s̶i̶n̶g̶ ̶n̶e̶v̶e̶r̶t̶h̶e̶l̶e̶s̶s̶,̶ ̶i̶t̶ ̶w̶i̶l̶l̶ ̶h̶e̶l̶p̶ ̶m̶e̶ ̶!̶
Edit : Officially, thank you An-dz, it's working ! Thank you so much ! :D
Posts: 2,671
Thanks: 62741 in 190 Posts
So, the code:
import injector import sims4.resources from sims4.tuning.instance_manager import InstanceManager from sims4.resources import Types import services SNIPPET_ID = 24511 MIXER_ID = 13984173314801262715 @injector.inject_to(InstanceManager, 'load_data_into_class_instances') def add_mixer_to_snippet(original, self): # Let the original function finish up its work original(self) # This function gets called once for each tuning type, so by only running this if we're specifically # working on the snippets, we can just get that snippet directly using the key rather than looping # through all the instances to and checking the guids. The same tactic works for getting the tuning # straight for the mixer interaction directly from the affordance manager. if self.TYPE == Types.SNIPPET: # Get the snippet tuning key = sims4.resources.get_resource_key(SNIPPET_ID, Types.SNIPPET) snippet_tuning = self._tuned_classes.get(key) # Check that we found it.... # If we were running in a loop, we'd probably want to skip to the next iteration # rather than just returning if the snippet id is invalid. if snippet_tuning is None: return # Get the mixer from the affordance manager affordance_manager = services.affordance_manager() key = sims4.resources.get_resource_key(MIXER_ID, Types.INTERACTION) mixer_tuning = affordance_manager.get(key) if mixer_tuning is None: return # Add the mixer tuning to the snippet's value (a tuple) snippet_tuning.value = snippet_tuning.value + (mixer_tuning, )
Posts: 110
Thanks: 628 in 22 Posts
Posts: 11,006
Thanks: 423049 in 1121 Posts
This is the injector I use:
# Python function injection # By scripthoge at ModTheSims # from functools import wraps import inspect # method calling injection def inject(target_function, new_function): @wraps(target_function) def _inject(*args, **kwargs): return new_function(target_function, *args, **kwargs) return _inject # decarator injection. def inject_to(target_object, target_function_name): def _inject_to(new_function): target_function = getattr(target_object, target_function_name) setattr(target_object, target_function_name, inject(target_function, new_function)) return new_function return _inject_to def is_injectable(target_function, new_function): target_argspec = inspect.getargspec(target_function) new_argspec = inspect.getargspec(new_function) return len(target_argspec.args) == len(new_argspec.args) - 1
This is the stuff that I'm doing (I *did* manage to put the commas in the correct places now, at least =P)
import services import injector import objects import types pbox_swipe_sa_instance_ids = (10138399433229310871, ) pbox_swipe_object_ids = (24970, 75981, 24965, 24968, 24971, 24972, 24969, 24950, 24977, 75986, 75989, 24973, 74574, 75985, 38952, 24954, 75990, 10397, 76084, 99233, 76094, 76078, 73994, 99235, 75357, 76085, 76090, 76091, 76087, 76080, 76083, 76093, 76081, 76092, 76095, 76089, 76082, 76077, 73995, 76088, 14953, 74706, 27741, 74642, 74705, 74641, 74709, 100069, 74348, 27738, 29868, 29885, 29886, 29895, 29896, 29898, 29890, 38207, 38210, 27736, 27733, 27732, 74646, 27728, 97435, 27737, 36629, 36616, 74644, 74639, 36626, 36615, 74647, 74055, 27735, 30924, 27727, 74643, 77622, 74645, 74708, 27731, 27729, 27739, 74640, 99158, 14863, 27740, 74707, 27762, 24325, 27734, 36380, 77623, 74341, 27648, 14846, 74347, 31710, 27730, 36628, 14963, 36691, 75999, 76000, 76001, 76002, 33784, 102301, 33786, 33787, 33788, 33789, 33790, 33791, 33792, 33793, ) def attachinteraction(sa_list, affordance_manager, sa_id): tuning_class = affordance_manager.get(sa_id) if not tuning_class is None: sa_list.append(tuning_class) @injector.inject_to(objects.game_object.GameObject, 'on_add') def pbox_swipe_add_super_affordances(original, self): original(self) if not self.guid64 in pbox_swipe_object_ids: return sa_list = [] affordance_manager = services.affordance_manager() if(isinstance(pbox_swipe_sa_instance_ids, int)): attachinteraction(sa_list, affordance_manager, pbox_swipe_sa_instance_ids) else: for sa_id in pbox_swipe_sa_instance_ids: attachinteraction(sa_list, affordance_manager, sa_id) self._super_affordances = self._super_affordances + tuple(sa_list)
^^ Above is the stuff that IS working, albeit not very efficiently I guess. (The point is that sims can swipe craftables that don't belong to them, so that they can exchange stuff between households on the little farmer's market they built)
The way I read it, it would need to look like this now (but there is probably something missing):
from functools import wraps import zone def inject(target_function, new_function): @wraps(target_function) def _inject(*args, **kwargs): return new_function(target_function, *args, **kwargs) return _inject def inject_to(target_object, target_function_name): def _inject_to(new_function): target_function = getattr(target_object, target_function_name) setattr(target_object, target_function_name, inject(target_function, new_function)) return new_function return _inject_to
import services import injector import objects import types import zone pbox_swipe_sa_instance_ids = (10138399433229310871, ) pbox_swipe_object_ids = (24970, 75981, 24965, 24968, 24971, 24972, 24969, 24950, 24977, 75986, 75989, 24973, 74574, 75985, 38952, 24954, 75990, 10397, 76084, 99233, 76094, 76078, 73994, 99235, 75357, 76085, 76090, 76091, 76087, 76080, 76083, 76093, 76081, 76092, 76095, 76089, 76082, 76077, 73995, 76088, 14953, 74706, 27741, 74642, 74705, 74641, 74709, 100069, 74348, 27738, 29868, 29885, 29886, 29895, 29896, 29898, 29890, 38207, 38210, 27736, 27733, 27732, 74646, 27728, 97435, 27737, 36629, 36616, 74644, 74639, 36626, 36615, 74647, 74055, 27735, 30924, 27727, 74643, 77622, 74645, 74708, 27731, 27729, 27739, 74640, 99158, 14863, 27740, 74707, 27762, 24325, 27734, 36380, 77623, 74341, 27648, 14846, 74347, 31710, 27730, 36628, 14963, 36691, 75999, 76000, 76001, 76002, 33784, 102301, 33786, 33787, 33788, 33789, 33790, 33791, 33792, 33793, ) def attachinteraction(sa_list, affordance_manager, sa_id): tuning_class = affordance_manager.get(sa_id) if not tuning_class is None: sa_list.append(tuning_class) @inject_to(zone.Zone, 'on_loading_screen_animation_finished') def pbox_swipe_add_super_affordances(original, self): original(self) if not self.guid64 in pbox_swipe_object_ids: return sa_list = [] affordance_manager = services.affordance_manager() if(isinstance(pbox_swipe_sa_instance_ids, int)): attachinteraction(sa_list, affordance_manager, pbox_swipe_sa_instance_ids) else: for sa_id in pbox_swipe_sa_instance_ids: attachinteraction(sa_list, affordance_manager, sa_id) self._super_affordances = self._super_affordances + tuple(sa_list)
.. but that results in nonworkingness. What am I missing here?
(I guess I don't need to import the objects and types any more, actually .. I just left that in for now. Also, I don't actually need to import zone in the injector, or do I?)
Stuff for TS2 · TS3 · TS4 | Please do not PM me with technical questions – we have Create forums for that.
In the kingdom of the blind, do as the Romans do.
Posts: 2,671
Thanks: 62741 in 190 Posts
Yeah, there were a couple of issues with the code you had. Notably you were injecting into the wrong function to add the SAs to the tuning like Deaderpool demonstrated in post 33. This is probably what I will be using in the future - a combination of Deaderpool's method but using some of the new tricks I learned figuring out the mixer stuff above. Should be a slight bit faster as it doesn't loop through every loaded tuning looking at the guids. This should work for you as is (assuming I didn't make any mistakes!):
ETA - Added sims4.resources import
import services import injector import sims4.resources from sims4.tuning.instance_manager import InstanceManager from sims4.resources import Types pbox_swipe_sa_instance_ids = (10138399433229310871, ) pbox_swipe_object_ids = (24970, 75981, 24965, 24968, 24971, 24972, 24969, 24950, 24977, 75986, 75989, 24973, 74574, 75985, 38952, 24954, 75990, 10397, 76084, 99233, 76094, 76078, 73994, 99235, 75357, 76085, 76090, 76091, 76087, 76080, 76083, 76093, 76081, 76092, 76095, 76089, 76082, 76077, 73995, 76088, 14953, 74706, 27741, 74642, 74705, 74641, 74709, 100069, 74348, 27738, 29868, 29885, 29886, 29895, 29896, 29898, 29890, 38207, 38210, 27736, 27733, 27732, 74646, 27728, 97435, 27737, 36629, 36616, 74644, 74639, 36626, 36615, 74647, 74055, 27735, 30924, 27727, 74643, 77622, 74645, 74708, 27731, 27729, 27739, 74640, 99158, 14863, 27740, 74707, 27762, 24325, 27734, 36380, 77623, 74341, 27648, 14846, 74347, 31710, 27730, 36628, 14963, 36691, 75999, 76000, 76001, 76002, 33784, 102301, 33786, 33787, 33788, 33789, 33790, 33791, 33792, 33793) @injector.inject_to(InstanceManager, 'load_data_into_class_instances') def add_superaffordances(original, self): original(self) if self.TYPE == Types.OBJECT: # First, get a tuple containing the tunings for all the super affordances... affordance_manager = services.affordance_manager() sa_list = [] for sa_id in pbox_swipe_sa_instance_ids: key = sims4.resources.get_resource_key(sa_id, Types.INTERACTION) sa_tuning = affordance_manager.get(key) if not sa_tuning is None: sa_list.append(sa_tuning) sa_tuple = tuple(sa_list) # Now update the tunings for all the objects in our object list for obj_id in pbox_swipe_object_ids: key = sims4.resources.get_resource_key(obj_id, Types.OBJECT) obj_tuning = self._tuned_classes.get(key) if not obj_tuning is None: obj_tuning._super_affordances = obj_tuning._super_affordances + sa_tuple
Posts: 11,006
Thanks: 423049 in 1121 Posts
Notably you were injecting into the wrong function |
.. DUH!
Yeah now I see it too ..
Thank you. It all makes sense now. Your code is so .. readable! =)
I'll test it out later, will let you know if I still see issues with it!
Stuff for TS2 · TS3 · TS4 | Please do not PM me with technical questions – we have Create forums for that.
In the kingdom of the blind, do as the Romans do.
Posts: 11,006
Thanks: 423049 in 1121 Posts
Changes I made:
- Renamed my injector.py to swipeinjector.py since the LastException was suddenly also complaining about graycurse's cooking ingredients mod (which it has never had any issues with before, so I guess it now got confused which injector the swipeit was talking about?)
- Added import sims4.resources to the swipeit.py -- my train of thought was "does this know what 'get_resource_key' means when I only import Types? Maybe it doesn't so I'm just gonna feed it the whole thing and see if it works then" (which I realise may still be a little bit sub-optimal)
Haven't done a lot of testing yet but the same lot that didn't load before now does, and the sims who live there can also swipe all the stuff that other sims have dropped onto that lot (which they wouldn't be able to by default).
Stuff for TS2 · TS3 · TS4 | Please do not PM me with technical questions – we have Create forums for that.
In the kingdom of the blind, do as the Romans do.
Posts: 2,671
Thanks: 62741 in 190 Posts
Okay, initially your code did not work Changes I made: - Renamed my injector.py to swipeinjector.py since the LastException was suddenly also complaining about graycurse's cooking ingredients mod (which it has never had any issues with before, so I guess it now got confused which injector the swipeit was talking about?) |
If you have two modules named identically only one of them will be used, so yes, if you need a customized injector it would have to use a different module name. Deaderpool has a really nice one he's whipped up that catches issues that may arise when EA updates a function to have different parameters.
- Added import sims4.resources to the swipeit.py -- my train of thought was "does this know what 'get_resource_key' means when I only import Types? Maybe it doesn't so I'm just gonna feed it the whole thing and see if it works then" (which I realise may still be a little bit sub-optimal) |
Hmm, at first I thought, "Duh how could I have forgotten that?" and then looked at my code and it's not importing sims4.resources. Not sure why it's working, maybe something else I was importing in my quick tests script file (which is full of a lot of random garbage, lol) imports that module. I'll edit the post to add that import.
Posts: 11,006
Thanks: 423049 in 1121 Posts
if you need a customized injector |
Actually I haven't tried to use the first one again -- maybe that would have worked too if it hadn't been for the missing import. I only changed it to the second one (of the two I quoted above) because that was what Deaderpool quoted in post #36 above. Possibly that caused an issue with the greycurse thing which was suddenly missing the is_injectable .. I guess?
But wouldn't it make sense on the long run when people give their injectors individual names anyway? Right now it's like we're all silently relying on them all being identical ..
Stuff for TS2 · TS3 · TS4 | Please do not PM me with technical questions – we have Create forums for that.
In the kingdom of the blind, do as the Romans do.
Posts: 123
Thanks: 2254 in 12 Posts
Thanks
Posts: 2,671
Thanks: 62741 in 190 Posts
As it does to add an interaction to an NPC that I created ? I created an interaction and managed to make it work, but I wanted this interaction appeared only for a job that I created ... (sorry my bad english , I'm using google translator ) |
Someone who's worked more with these might have better info, but an NPC job is usually attached to a "situation". You can add code in the XML for an interaction to make it only occur if the NPC is running that situation... but there might be a better way.
You may want to open a new thread for this question, as it doesn't appear to deal with the topic of adding the affordances via Python.
Who Posted
|