#1
20th Sep 2014 at 1:25 AM
Last edited by justJones : 6th May 2018 at
11:20 PM.
Make a mod that runs a function when the game is loaded!
Hi there! This is the first Python tutorial I write (heh, I just started learning the language some days ago), so please bear with my babbling.
OK, so let's get started...
What You'll Need- A working knowledge of Python (something above the print('hello world') stage; you need to know about callbacks, all the types of arguments that can be passed in a function, and the general OOP style of Python)
- The Sims 4's Python code (decompiled)
First things first, let's import 'sims4'. The sims4 directory in "core" is a module on its own containing other modules. So yes, you can import a directory name here!
Note how I didn't write "import core.sims4" (since the "sims4" folder is found inside another folder called "core"). When TS4 loads, it loads "core", "base", and "simulation" as one big root, meaning that you don't need to actually specify these names when including their modules in your scripts. Imagine all three of these being in one giant directory instead!
Now, let's define the function we would like to... Oh, wait a minute! Slow down, there! We need to declare something before that.
You see, when you register a callback for running a function in-game, for some reason, the function will continue to be called almost non-stop. This will bog down the game considerably, and ideally, you don't want your function to be called 20 times a second in the game. To prevent this, we need a decorator (one of those things with "@" that sit right above certain functions) to tell the function we declare to run only once. We first need to declare our decorator. Let's call it "run_once" and declare it with a function as an argument.
Code:
def run_once(function):
def wrapper(*args, **kwargs):
if not wrapper.has_run:
wrapper.has_run = True
return function(*args, **kwargs)
wrapper.has_run = False
return wrapper
See what I did there? I declared a function that passes another function as an argument within a "wrapper" that takes the function's own arguments. If the function has run before, then it will return "None".
Now, let's declare our function with run_once as the decorator:
Code:
@run_once
def foo():
#insert whatever you want here
return 0
OK, so that's not much of a function, but I have confidence that your imagination is better than mine!
It's at this point, and at this point only, that you can register the callback. You see, TS4 will just bug out with a lastException.txt if you don't declare a function before you register it as a callback. Despite the non-linear nature of Python, I found this change of pace weird, but familiar, reminding me of C++.
So, you want to register the callback now using sims4's callback_utils. Let's register our little function as something that the game should call when it's done processing events for the household...
Code:
sims4.callback_utils.add_callbacks(sims4.callback_utils.CallbackEvent.PROCESS_EVENTS_FOR_HOUSEHOLD_EXIT, foo)
That's it! We've finished registering the callback! This is how the complete code looks like now:
Code:
import sims4
def run_once(function):
def wrapper(*args, **kwargs):
if not wrapper.has_run:
wrapper.has_run = True
return function(*args, **kwargs)
wrapper.has_run = False
return wrapper
@run_once
def foo():
#insert whatever you want here
return 0
sims4.callback_utils.add_callbacks(sims4.callback_utils.CallbackEvent.PROCESS_EVENTS_FOR_HOUSEHOLD_EXIT, foo)
If you'd like an example of how this is used in a real mod, go ahead and check out doreldecay.py in my new relationship decay mod (
here)!
I hope this tutorial was as informative as I have intended it to be!