- Site Map >
- Modding and Creation >
- Sims 4 Creation >
- Modding Discussion >
- Creating a scripting mod that modifies Maxis scripts
- Site Map >
- Modding and Creation >
- Sims 4 Creation >
- Modding Discussion >
- Creating a scripting mod that modifies Maxis scripts
Replies: 7 (Who?), Viewed: 957 times.
#1
28th Mar 2015 at 5:24 PM
Posts: 68
Thanks: 5635 in 103 Posts
Creating a scripting mod that modifies Maxis scripts
Hi all.As you may or may not know, I am the author of the "No More Culling" mod, which has until the latest patch been successful at disabling the game's culling feature with a modified StoryProgressionService XML. The problem is that, since the new patch, this method of stopping culling no longer works, as EA has added new functionality that calls the culling action upon loading a save.
After hacking through the game's Python files, I've managed to find a way to disable this new function, by directly editing the Max Population action in the Simulation/Story Progression/actions.pyo file. I can get my edited version into my game by replacing the original script and making a new Simulation.zip, which does work in my game, but obviously this is not how I want to deliver the mod to the general public.
I'm not really knowledgeable at all when it comes to Python, and I was wondering how I could inject the modified StoryProgressionActionMaxPopulation class (specifically def process_action) into the game via the means of a standard mod, without directly replacing the game's core files. Ideally, I want the game to access my modified code instead of its own, but all the user would have to do is place a file into their Mods folder.
Can anyone help?
Attached files:
actions.pyo.zip (4.5 KB, 17 downloads) - View custom content | ||
22872 03-29-2015 02:51 actions.pyo.py --------- ------- 22872 1 file |
Advertisement
#2
28th Mar 2015 at 9:58 PM
Posts: 6,939
Thanks: 7556 in 405 Posts
Hey, I'll take a stab at this. I am guessing you've added your logic to the StoryProgressionActionMaxPopulation class. It looks like you changed, "process_action". You can use an "injector.py" definition that you can find in use in many mods. I don't know if he originally wrote it or it came from somewhere else, but scumbumbo is who I consider an expert on the usage of this stuff (more so than myself for sure!)
It normally looks like this:
Then, in your own mod, you'd have a python file that would look something like this:
The problem is, you're actually wanting to override that method and take some functionality out of it. You could do that by just adding all of the function exactly like it is in the "process_action" into your own function in your mod script and then leave-out the part you don't want. But, if EA changes that function in the future, you're going to have to incorporate any changes they make as well. You may have better luck if you inject to the "_get_culling_score" function and change it to always return 0. I believe if you do that, then the code you're leaving out of your version of actions.py could be left in.
Maybe something like this:
You could add some additional logic in there of your own so you're not ALWAYS returning zero. But I think that's the basic idea. Also, there may be concerns about the fact we're injecting around a "private" definition. I'm not smart enough on Python to be able to answer that one for you.
It normally looks like this:
Code:
from functools import wraps 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
Then, in your own mod, you'd have a python file that would look something like this:
Code:
from injector import inject_to import story_progression.actions @inject_to(story_progression.actions.StoryProgressionActionMaxPopulation , 'process_action') def inject_process_action(original, self, story_progression_flags) original(self, story_progression_flags)
The problem is, you're actually wanting to override that method and take some functionality out of it. You could do that by just adding all of the function exactly like it is in the "process_action" into your own function in your mod script and then leave-out the part you don't want. But, if EA changes that function in the future, you're going to have to incorporate any changes they make as well. You may have better luck if you inject to the "_get_culling_score" function and change it to always return 0. I believe if you do that, then the code you're leaving out of your version of actions.py could be left in.
Maybe something like this:
Code:
from injector import inject_to import story_progression.actions @inject_to(story_progression.actions.StoryProgressionActionMaxPopulation , '_get_culling_score') def inject_get_culling_score(original, self, sim_info) original(self, sim_info) # This would normally be used to get the culling score for process_action to use return 0 # Instead, we're going to always return 0
You could add some additional logic in there of your own so you're not ALWAYS returning zero. But I think that's the basic idea. Also, there may be concerns about the fact we're injecting around a "private" definition. I'm not smart enough on Python to be able to answer that one for you.
#3
28th Mar 2015 at 10:10 PM
Posts: 6,939
Thanks: 7556 in 405 Posts
Looking at it a bit closer, that may not be exactly what you want as we'll still be appending zeroes to the self._household_scores so when they do the process_action it will still cull I think. Maybe what I've described there will help you, though, just as far as how to inject into that action. It actually looks like if you just make it so all SimInfo have can_be_culled to false, then they won't be culled ever. So, maybe that's alternate place to look rather than the StoryProgressionActionMaxPopulation action. This is going a lot deeper than you may want to go and it may just be easier to do like you were looking at originally and just override the "process_action". This code really does cause anything to happen now in "process_action", though, so I almost question why you'd make that override do anything other than just "pass" or "return".
There's several other ways this could go. You could override can_be_culled so it is always false. There is a trait that can be set on sims so they are immune to culling. You could change household type so they are not "is_persistent_npc".
Anyway. Hopefully I haven't harmed more than helped! Good luck!
Code:
from injector import inject_to import story_progression.actions import services @inject_to(story_progression.actions.StoryProgressionActionMaxPopulation , 'process_action') def inject_process_action(original, self, story_progression_flags) self._household_scores.clear() for sim_info in services.sim_info_manager().values(): while sim_info.can_be_culled(): culling_score = self._get_culling_score(sim_info) self._household_scores[sim_info.household.id].append(culling_score)
There's several other ways this could go. You could override can_be_culled so it is always false. There is a trait that can be set on sims so they are immune to culling. You could change household type so they are not "is_persistent_npc".
Anyway. Hopefully I haven't harmed more than helped! Good luck!
#4
29th Mar 2015 at 6:08 AM
Last edited by Dark Gaia : 29th Mar 2015 at 6:55 AM.
Posts: 68
Thanks: 5635 in 103 Posts
Hi. This is exactly what I'm looking for. I think I may be able to cobble something together based on what you've shown here - what I was really looking for was a way to inject the code. There's always a chance that EA will modify the function in the future, but as long as I know how to inject my own code, I don't mind having to update it as it's only deleting one argument from the definition.
The other methods you've posted would probably work as a more permanent fix, which I'll likely look into as well, but at the moment a simple workaround would probably make a lot of people very happy.
Thanks so much for your help
EDIT: I was getting an invalid syntax error with the code above, but it's fixed now
The other methods you've posted would probably work as a more permanent fix, which I'll likely look into as well, but at the moment a simple workaround would probably make a lot of people very happy.
Thanks so much for your help
EDIT: I was getting an invalid syntax error with the code above, but it's fixed now
#5
29th Mar 2015 at 6:53 AM
Posts: 2,671
Thanks: 62741 in 190 Posts
Another option, since you're looking to completely eliminate the process, would be to just override one of the object methods completely. Something like this should do the trick, I'd target the process_action and just skip it entirely so it doesn't even waste time trying to build a cull list....
The injection method (I nabbed it from scripthoge btw) is primarily for if you want to make sure the original code runs and then do something else afterwards.
Code:
As soon as the above is loaded (untested, assuming I didn't make any typos which is unlikely as I was up until 5am last night working on a crashed hard drive) it should just completely replace the method - no need to mess around with injection.#import story_progression.actions def my_storyprogression_process_action(self, story_progression_flags): return story_progression.actions.StoryProgressionActionMaxPopulation.process_action = my_storyprogression_process_action
The injection method (I nabbed it from scripthoge btw) is primarily for if you want to make sure the original code runs and then do something else afterwards.
#6
29th Mar 2015 at 6:54 AM
Last edited by Dark Gaia : 29th Mar 2015 at 7:05 AM.
Posts: 68
Thanks: 5635 in 103 Posts
Thanks Scumbumbo! This looks like the best option. I'll test out all of these different methods and see which one I prefer.
EDIT: Your method seems to be the fastest, performance wise. Since the game is no longer calculating culling scores, it actually runs a little faster.
EDIT: Your method seems to be the fastest, performance wise. Since the game is no longer calculating culling scores, it actually runs a little faster.
#7
29th Mar 2015 at 7:16 AM
Posts: 2,671
Thanks: 62741 in 190 Posts
Glad it seems to be working well for you!
#8
29th Mar 2015 at 7:10 PM
Posts: 6,939
Thanks: 7556 in 405 Posts
That's a nifty thing to know! I can probably use that as well! Thanks!
Who Posted
|