- 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: 62709 in 190 Posts
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 .. |
I would say only if the author is diverging from the initial injector that Scripthoge wrote, but then I'm pretty ambivalent about it. I guess I got into bad habits from when there were no more than a handful of us doing script modding, but now that it's getting more widespread maybe that's a good idea - not only for the injector, but for any module name. It could cause issues with any module, say two modders both used the filename 'modify_pregnancy.pyo' one would get loaded and the other ignored.
Who has the clout to make an "official specification" for module names? I think I recall someone suggesting something for function names, but since they won't conflict being in separate modules, function names really don't matter much. At least from what I understand of Python namespaces. (Someone feel free to correct me if I'm wrong. I'm not proud. Just ambivalent.)
Posts: 11,006
Thanks: 422946 in 1121 Posts
I would say only if the author is diverging from the initial injector that Scripthoge wrote |
Yes, that’s what I meant – as long as it’s identical it wouldn’t matter of course =)
Good point about the module names in general. I don’t think there’s a need for a lot of officialness .. I for one have learned a lot more from looking at existing mods or code samples that people post in forums, than from any sort of theoretical/general advice. Since every other script mod I’ve seen cites a certain scumbumbo as their “good example”, maybe simply start naming your modules “scumbumbo_dostuff”? The practice of including creator tags in all sorts of names is common enough, I think, for people to realise it might be a good idea to name theirs “pbox_dostuff” instead, without anyone needing to explicitely tell them that.
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,682
Thanks: 9680 in 11 Posts
"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Posts: 11,006
Thanks: 422946 in 1121 Posts
Why isn't there a community edition injector |
If you want that kind of thing, why don't you make it? This is Create, not Request ..
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,682
Thanks: 9680 in 11 Posts
<I c="AffordanceFilter" i="snippet" m="snippets" n="af_Retail_Item_Sold" s="110412"> <U n="value"> <V n="default_inclusion" t="exclude_all"> <U n="exclude_all"> <L n="include_affordances"> <T>108686<!--retail_RestockItem--></T> <T>111859<!--si_Retail_ClearSign--></T> <T>115687<!--retail_RestockItem_Instant--></T> </L> </U> </V> </U> </I>
Not sure how to go about expressing the nestedness?
"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Posts: 2,671
Thanks: 62709 in 190 Posts
Ok so been looking at the alternative use of injection in this thread, injecting to a snippet. The example was a simple one, where we just wanted to add to a top level value list. I have to inject to a snippet that looks like this: <snip> Not sure how to go about expressing the nestedness? |
This should be fairly simple, but rather than just figuring this out I'd like to do a, maybe not full blown tutorial, but at least a guide on how I figure these things out. I don't have the time at the moment, but would likely be able to get this done over the weekend at some point - might take more time, it depends largely on how in depth I end up making it.
The short answer is that it's just a matter of tapping into the load_data_into_class_instances method, getting the tuning you want using the key and self._tuned_classes.get() and then using the Python inspect module to dump the various variables in that tuning. In this instance it will be necessary to then use that knowledge gained to dump the variables from the nested tunings, rinse and repeat until we get down to the variable that needs to be modified and since the target variable should just be an ordinary tuple then we can just add to that tuple the items you want to add.
Posts: 11,682
Thanks: 9680 in 11 Posts
"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Posts: 11,682
Thanks: 9680 in 11 Posts
"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Posts: 2,671
Thanks: 62709 in 190 Posts
Posts: 11,682
Thanks: 9680 in 11 Posts
"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Posts: 2,671
Thanks: 62709 in 190 Posts
If you have an example one or two lines for the recursion into the keys/tags that might be enough to get me started? |
Sorry, I've been so busy, but you've reached "Scumbumbo's Project of the Day" status.
And the mini-guide (which turned out to be quite long of course) is written, see the thread for it here.
Try and keep any questions organized, if you have a question about the inspection process itself, ask there - but if you have a question about using the results of the inspection process ask in a new thread or here, whichever seems appropriate.
Posts: 11,682
Thanks: 9680 in 11 Posts
"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Posts: 179
Thanks: 1 in 1 Posts
There's been a lot of experimentation since this was originally posted, so I thought it might be nice to add a list of important posts from the thread:
Original Message This should be a useful addition to the collective arsenal, allowing the ability to add interactions for sims without requiring overriding the _super_affordances tuning in the object_sim resource. This allows for multiple mods to add interactions for sims without conflict. treelife brought this up recently in a thread, although I believe that was in reference to objects. This method works for just sims, but should be able to be adapted to objects as well. A sim is just a special object after all, the only difficulty with objects may be to identify the proper object to add the affordances to, but that shouldn't be too major. There had been a thread about this earlier, and although it was agreed that scripting might be a good idea to overcome conflicts, I don't know that anyone made much progress at it. It seemed straightforward, but without knowing how all the internals actually worked it wasn't at all. After a lot of false tries, and a lot of poking through the source, and a lot of examing the outputs from vars() on objects, I finally had some success. Turns out it is actually fairly easy! The super affordance tuning classes available to a sim are stored as a tuple in the sim object called _super_affordances. All that remained was to find a way to query the affordance manager to get the tuning classes we want to add to the sim. There may be a better way to do this, but I'm just relieved to have figured this method out in the short span of 10 or 15 hours worth of work. First, create the XML for all the interactions, pie menu categories, etc. Create a tuple of all the instance ids for all of your interactions - the numbers you would normally add to the _super_affordances list in the XML. Then a little bit of Python, injected into the sims on_add() method will do the trick:
Code:
import services import injector YOURMODNAME_sa_instance_ids = (17608706005782878675, 13258313599595875556, ...etc for all the interactions you're adding) @injector.inject_to(sims.sim.Sim, 'on_add') def YOURMODNAME_add_super_affordances(original, self): original(self) sa_list = [] affordance_manager = services.affordance_manager() for sa_id in YOURMODNAME_sa_instance_ids: tuning_class = affordance_manager.get(sa_id) if not tuning_class is None: sa_list.append(tuning_class) self._super_affordances = self._super_affordances + tuple(sa_list) That's all there is to it. You'll also need the injector.py script, but several script mods have this already to get a copy of - e.g. Always Start Lots Pause Obviously, as much time as I've spent on getting this working, I'd appreciate credit for anyone who uses this. |
So is there an actual list of export tunes or super affordances? There are never ending options, it's just confusing.
Posts: 51
Posts: 91
Thanks: 2578 in 6 Posts
import services
import injector
import objects.game_object
REXTVguide_TV_sa_instance_ids = (1956218958,)
REXTVguide_TV_object_ids = (14995, 39355, 77515, 14996, 36924, 39353, 36923, 77551, 77552, 36926, 36925)
@injector.inject_to(objects.game_object.GameObject, 'on_add')
def REXTVguide_TV_add_super_affordances(original, self):
original(self)
sa_list = []
affordance_manager = services.affordance_manager()
for sa_id in REXTVguide_TV_sa_instance_ids:
tuning_class = affordance_manager.get(sa_id)
if not tuning_class is None:
sa_list.append(tuning_class)
self._super_affordances = self._super_affordances + tuple(sa_list)
Just edited it so I can ask if I should also include the Television.py and Pick_channel.py
But they showed up on TV actually. I'm confused
Posts: 29
I don't even care right now about the best, most efficient way to do this. For now, I just want to see something work.
As a test, I basically made a straight copy of the tuning file for sim_TalkToSelf, since unless I'm mistaken, it's always available from the active Sim actor's top level context menu. I changed the sim_TalkToImaginaryFriend, updated the instance ID with the decimal version of the 64-bit FNV-1 hash of thewatcheruatu:sim_talktoself with the high bit set, and I think the only thing I changed was for testing purposes, I altered the trait whitelist from the insane trait to the bookworm trait (27916). I mean, it seemed like a pretty foolproof way to test how a custom interaction might work. Oh, and I used s4pe to create an STBL file (as explained here: http://modthesims.info/showthread.php?t=573055) just for the name of the interaction.
Packaged them, put them in the mod directly, was kind of hoping it would just work, and then discovered that I guess I have to script inject custom interactions into the game(?).
Okay, I can do that. So I came to this thread--didn't really care about performance at this point--grabbed injector.pyo, created another script with the following code, compiled it, bundled them in a ts4script archive, and moved it into the mod directory:
import services import injector import sims.sim interaction_test_sa_instance_ids = (12191329784171701135) @ injector.inject_to(sims.sim.Sim, 'on_add') def interaction_test_add_super_affordances(original, self): original(self) sa_list = [] affordance_manager = services.affordance_manager() for sa_id in interaction_test_sa_instance_ids: tuning_class = affordance_manager.get(sa_id) if not tuning_class is None: sa_list.append(tuning_class) self._super_affordances = self._super_affordances + tuple(sa_list)
What this did was cause my game to hang upon attempting to load the world. So I assume I did something wrong.
Would really be appreciative if somebody could give me a nudge in the right direction.
Edit: I tried it again, this time with Deaderpool's code from post #33 in this thread, changing only these two lines:
sa_instance_ids = (12191329784171701135) object_ids = (sims.sim.Sim)
Sadly, it still didn't work, but the game didn't hang on load this time. Clearly, I'm missing something that is probably completely obvious.
Posts: 559
Thanks: 3205 in 6 Posts
I don't suppose anybody is willing to explain this to me like I'm five? I pretty much just started messing around with modding TS4 today. I mean, I've programmed extensively in other languages, but never Python and never for game modding purposes, and I'm getting a little bleary eyed from looking at XML and code all day. I'm trying to learn, though. I don't even care right now about the best, most efficient way to do this. For now, I just want to see something work. As a test, I basically made a straight copy of the tuning file for sim_TalkToSelf, since unless I'm mistaken, it's always available from the active Sim actor's top level context menu. I changed the sim_TalkToImaginaryFriend, updated the instance ID with the decimal version of the 64-bit FNV-1 hash of thewatcheruatu:sim_talktoself with the high bit set, and I think the only thing I changed was for testing purposes, I altered the trait whitelist from the insane trait to the bookworm trait (27916). I mean, it seemed like a pretty foolproof way to test how a custom interaction might work. Oh, and I used s4pe to create an STBL file (as explained here: http://modthesims.info/showthread.php?t=573055) just for the name of the interaction. Packaged them, put them in the mod directly, was kind of hoping it would just work, and then discovered that I guess I have to script inject custom interactions into the game(?). Okay, I can do that. So I came to this thread--didn't really care about performance at this point--grabbed injector.pyo, created another script with the following code, compiled it, bundled them in a ts4script archive, and moved it into the mod directory:
Code:
import services import injector import sims.sim interaction_test_sa_instance_ids = (12191329784171701135) @ injector.inject_to(sims.sim.Sim, 'on_add') def interaction_test_add_super_affordances(original, self): original(self) sa_list = [] affordance_manager = services.affordance_manager() for sa_id in interaction_test_sa_instance_ids: tuning_class = affordance_manager.get(sa_id) if not tuning_class is None: sa_list.append(tuning_class) self._super_affordances = self._super_affordances + tuple(sa_list) What this did was cause my game to hang upon attempting to load the world. So I assume I did something wrong. Would really be appreciative if somebody could give me a nudge in the right direction. Edit: I tried it again, this time with Deaderpool's code from post #33 in this thread, changing only these two lines:
Code:
sa_instance_ids = (12191329784171701135) object_ids = (sims.sim.Sim) Sadly, it still didn't work, but the game didn't hang on load this time. Clearly, I'm missing something that is probably completely obvious. |
sa_instance_ids = (12191329784171701135, )
Posts: 29
sa_instance_ids = (12191329784171701135, ) |
Oh, that's interesting. I guess I needed to read up on tuples in Python.
With that said, I fixed that for both the sa_ids and the object_ids, and sadly, it still isn't working. Is there any easy way to test whether the script it the problem or if it's the interaction tuning file?
Edit: Nevermind. I went back to the code from the very first post in this thread and got it to work this time. I mean, I'd still like to optimize it, so I'll explore that later, and may be back! But at least I know the tuning file is good!
Posts: 8
Posts: 3
Posts: 11
Posts: 49
@blubber - 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. |
I cant get past the STBL 0x220557DA I added the group and 0x00918772618D89D4 as the tutorial says just like in the picture, but the ok button stays dark in s4pe. I click on the stbl drop down tab list and reselect it then it says ok but before I can push it it says there is an unhandled exception in the program and must close. what can I do?
Posts: 2,671
Thanks: 62709 in 190 Posts
I cant get past the STBL 0x220557DA I added the group and 0x00918772618D89D4 as the tutorial says just like in the picture, but the ok button stays dark in s4pe. I click on the stbl drop down tab list and reselect it then it says ok but before I can push it it says there is an unhandled exception in the program and must close. what can I do? |
Make sure you're using the most recent version of s4pe, it should work fine. I've read that you need to use 7zip to decompress the s4pe archive, although I've never had a problem using WinRAR myself it is a known problem with some archive programs not being able to decompress the 7z files.
ETA: Also, in hindsight, that tutorial needs some rewriting. The s4pe string table editing process (after you hopefully get past the step where it's crashing on you) has changed. Also the XML interaction file will need rewriting as there is no longer such a thing as the CommandSuperInteraction class in the game - it will have to be rewritten as an ImmediateSuperInteraction. Maybe I can get to that this weekend after the Thanksgiving holiday.
Posts: 49
Make sure you're using the most recent version of s4pe, it should work fine. I've read that you need to use 7zip to decompress the s4pe archive, although I've never had a problem using WinRAR myself it is a known problem with some archive programs not being able to decompress the 7z files. ETA: Also, in hindsight, that tutorial needs some rewriting. The s4pe string table editing process (after you hopefully get past the step where it's crashing on you) has changed. Also the XML interaction file will need rewriting as there is no longer such a thing as the CommandSuperInteraction class in the game - it will have to be rewritten as an ImmediateSuperInteraction. Maybe I can get to that this weekend after the Thanksgiving holiday. |
sounds like a plan, hope you all have a happy thanksgiving this year. I will go ahead and unistall and reinstall s4pe, just to make sure that wasn't the problem. its possible.
Posts: 49
I was talking to Scumbumbo about this and noticed that when attaching to Sims or Objects through on_add, you are doing it every time they are instanced, which could cause an impact in performance. So, I have been looking into doing a way of doing it once. What I came-up with is injecting into the instance manager right after all the tuning files are loaded. At that point, you can add your affordances to the object tuning files and they'll be there for the rest of the time the game is running. It may cause a slightly longer loading time, but once you're to the main menu, it should not have to be added to the objects again. The injection works like this:
Code:
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:
Code:
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) |
I keep getting errors when trying to add STBL 0x220557DA using scumbumbo mailbox tutorial as directed in the early steps. I tried reinstalling S4PE using the correct 7-zip method and still get the error. I want to move forward in the learning process but am stumped here as it closes the program when attempting. If you dont use the add function, what do you use instead?
Who Posted
|