Automatically Add Traits to All Sims
Aug 28, 2020 22:30:52 GMT -5
fufu508, MizoreYukii, and 4 more like this
Post by frankk on Aug 28, 2020 22:30:52 GMT -5
This tutorial has been updated!
Please view the updated tutorial on Medium.
----------
Are you making a mod that has custom traits? Do any of those traits need to be automatically assigned to all of the sims in the game? This tutorial will show you one quick way to achieve that. It is assumed that you already have your custom trait(s) made, and that you are at least somewhat familiar with Python scripting.
Part 1: Get an injector
Injectors are used to insert your code into the game's code. If you already have an injector, skip this step. If not, you can use the following:
This will be used to append your script to existing scripts in the game.
Part 2: Define your trait injector
You'll need a function that determines which traits to add to whom. This will of course vary heavily depending on your needs, but you can base your function off of the following:
Of course, replace `YOUR_TRAIT_NAME` with a name to store your trait in, and create more variables for however traits you would like to add. In order to assign these variables their values, you have to use the traits' decimal tuning ID in place of `YOUR_TRAIT_TUNING_ID`.
Replace `YOUR_CONDITIONS_HERE` with anything you'd like to filter sims by (i.e. if your trait only applies to humans, then add a condition here that checks if the `sim_info` is not a human). You'll have to do some digging into the `sims.sim_info` module to know everything that's possible to do here, but as a couple of examples:
Please note: I highly recommend that you include a condition to check if the `sim_info` has already had your trait(s) added to it. This will prevent the game from trying to add the same traits again, which will improve performance. You can do that like this:
Of course, add more `sim_info.has_trait(YOUR_TRAIT_NAME)` cases for each trait you're adding, or create a helper function to make your code more readable and concise.
The last part is there for if you have multiple traits that should be applied to different sims after the list is filtered. For example:
There are more interesting kinds of checks you can do, too:
As you can see, this portion is very open for customization. You can even change this code to make it work for something other than traits - that is just the example I am using here!
Part 3: Set your code's entry point
Everything you wrote so far is great, but it will not work until you hook it up to the game's code. You can do that with the following:
This entry point will trigger every time a sim is loaded into the world, and guarantees that every sim will have had your trait(s) added to them by the time you interact with them.
If need be, you can also load your traits to all sims after each loading screen with the following (but this is optional, and the former entry point should be enough for most cases):
Hopefully this tutorial helped you! If you have any questions, leave them on this thread.
---
EDIT 1: I edited this tutorial to make it more efficient. It now only applies the trait to a single sim as they are created, rather than iterating through all sims in the game every time a new sim is created.
EDIT 2: I realized there was a way to get rid of the entry point that adds the trait(s) to every sim on zone load. It's now even more efficient, as the code only runs for each individual sim as they are spawned in!
EDIT 3: I included the code that can load your traits after a screen load, in case anyone finds it useful.
Please view the updated tutorial on Medium.
----------
Are you making a mod that has custom traits? Do any of those traits need to be automatically assigned to all of the sims in the game? This tutorial will show you one quick way to achieve that. It is assumed that you already have your custom trait(s) made, and that you are at least somewhat familiar with Python scripting.
Part 1: Get an injector
Injectors are used to insert your code into the game's code. If you already have an injector, skip this step. If not, you can use the following:
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
This will be used to append your script to existing scripts in the game.
Part 2: Define your trait injector
You'll need a function that determines which traits to add to whom. This will of course vary heavily depending on your needs, but you can base your function off of the following:
import services
def CREATORNAME_MODNAME_add_traits_to_sim(sim_info):
trait_manager = services.trait_manager()
YOUR_TRAIT_NAME = trait_manager.get(YOUR_TRAIT_TUNING_ID) # add as many of these as you need
if sim_info is None or YOUR_CONDITIONS_HERE:
return
if CONDITIONS_FOR_1ST_TRAIT:
sim_info.add_trait(YOUR_1ST_TRAIT_NAME)
elif CONDITIONS_FOR_2ND_TRAIT:
sim_info.add_trait(YOUR_2ND_TRAIT_NAME)
else:
sim_info.add_trait(YOUR_3RD_TRAIT_NAME)
Of course, replace `YOUR_TRAIT_NAME` with a name to store your trait in, and create more variables for however traits you would like to add. In order to assign these variables their values, you have to use the traits' decimal tuning ID in place of `YOUR_TRAIT_TUNING_ID`.
Replace `YOUR_CONDITIONS_HERE` with anything you'd like to filter sims by (i.e. if your trait only applies to humans, then add a condition here that checks if the `sim_info` is not a human). You'll have to do some digging into the `sims.sim_info` module to know everything that's possible to do here, but as a couple of examples:
from sims.sim_info_types import Age
if sim_info is None or sim_info.age < Age.TEEN:
return
# this would make it so that your trait(s) can only be applied to sims that are teens or older
# keep in mind that this does NOT filter out animals or occults
from sims.sim_info_types import Age, Species
if sim_info is None or sim_info.age != Age.ADULT or sim_info.species != Species.HUMAN:
return
# this would make it so that your trait(s) can only be applied to adult, human sims
# keep in mind that this filters out animals, but NOT occults
Please note: I highly recommend that you include a condition to check if the `sim_info` has already had your trait(s) added to it. This will prevent the game from trying to add the same traits again, which will improve performance. You can do that like this:
if sim_info is None or sim_info.has_trait(YOUR_TRAIT_NAME):
return
Of course, add more `sim_info.has_trait(YOUR_TRAIT_NAME)` cases for each trait you're adding, or create a helper function to make your code more readable and concise.
The last part is there for if you have multiple traits that should be applied to different sims after the list is filtered. For example:
if sim_info.species == Species.CAT:
sim_info.add_trait(SOME_TRAIT_FOR_CATS)
elif sim_info.species == Species.DOG:
sim_info.add_trait(SOME_TRAIT_FOR_DOGS)
else:
sim_info.add_trait(SOME_TRAIT_FOR_EVERY_OTHER_SIM)
There are more interesting kinds of checks you can do, too:
# this would add your trait to all sims with an existing trait
if sim_info.has_trait(trait_manager.get(SOME_TRAIT_TUNING_ID)):
sim_info.add_trait(YOUR_TRAIT_NAME)
As you can see, this portion is very open for customization. You can even change this code to make it work for something other than traits - that is just the example I am using here!
Part 3: Set your code's entry point
Everything you wrote so far is great, but it will not work until you hook it up to the game's code. You can do that with the following:
from sims.sim import Sim
# this will call the function you defined in Step 2 each time a new sim is spawned in)
@inject_to(Sim, 'on_add')
def CREATORNAME_MODNAME_add_traits_after_sim_spawned(original, self, *args, **kwargs):
result = original(self, *args, **kwargs)
try:
CREATORNAME_MODNAME_add_traits_to_sim(self.sim_info)
except Exception as e:
# use a custom error logger here if you have one, if not, just use the below line
raise Exception(f"Error with MODNAME by CREATORNAME: {str(e)}")
return result
This entry point will trigger every time a sim is loaded into the world, and guarantees that every sim will have had your trait(s) added to them by the time you interact with them.
If need be, you can also load your traits to all sims after each loading screen with the following (but this is optional, and the former entry point should be enough for most cases):
from sims.sim_info_manager import SimInfoManager
@inject_to(SimInfoManager, 'on_loading_screen_animation_finished')
def CREATORNAME_MODNAME_add_traits_after_load(original, self, *args, **kwargs):
result = original(self, *args, **kwargs)
try:
for sim_info in services.sim_info_manager().get_all():
CREATORNAME_MODNAME_add_traits_to_sim(sim_info)
except Exception as e:
# use a custom error logger here if you have one, if not, just use the below line
raise Exception(f"Error with MODNAME by CREATORNAME: {str(e)}")
return result
Hopefully this tutorial helped you! If you have any questions, leave them on this thread.
---
EDIT 1: I edited this tutorial to make it more efficient. It now only applies the trait to a single sim as they are created, rather than iterating through all sims in the game every time a new sim is created.
EDIT 2: I realized there was a way to get rid of the entry point that adds the trait(s) to every sim on zone load. It's now even more efficient, as the code only runs for each individual sim as they are spawned in!
EDIT 3: I included the code that can load your traits after a screen load, in case anyone finds it useful.