PDA

View Full Version : Make a mod that runs a function when the game is loaded!


mgomez
20th Sep 2014, 1:25 AM
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!

import sims4

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.

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:

@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...

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:

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 (http://modthesims.info/download.php?t=534925))!

I hope this tutorial was as informative as I have intended it to be!

Vinathi
22nd Dec 2014, 5:53 PM
Could you explain callbacks a little bit more? Why do you need to include one at the end?

mgomez
9th Jan 2015, 8:13 PM
Could you explain callbacks a little bit more? Why do you need to include one at the end?

Hi, Vinathi. I just saw your message, and this post. I will reply with a little more detail there than over here, since more things were asked. However, I will answer the above question here in particular. :)

Callbacks are routines that are run when another routine is run. Let's say, for example, that Paul has to brush his hair in the morning (because he has relatively long hair and it gets into a deplorable state overnight while he tosses and turns in bed).

There are two functions right now: wake_up() and brush_hair(). They're separate functions, meaning that they have no connection to one another. Normally, in our lives, we make connections between the things we do and can run them in a sequence because we're human beings. Since computers are relatively stupid without our help, we have to make that connection for them. Paul has to brush his hair after he wakes up. To make it clear to the computer running the code that we want him to do this, we have to register a callback that runs the brush_hair() function right after wake_up() is run.

Once this is done, the computer "listens" to the sequence of code being run continuously and waits for wake_up() to be called. Because the callback to brush_hair() has been registered on the event that wake_up() is accessed, it will automatically run once wake_up() is finished. This is what callbacks do. I hope I explained it a little more clearly! :)

IceM
20th Dec 2019, 7:11 PM
sorry to be grave digging, but its hard to find documentations for the game.

The event PROCESS_EVENTS_FOR_HOUSEHOLD_EXIT means whenever you leave the current household , right?
if i want to load a funcion when i load the game, i should use PROCESS_EVENTS_FOR_HOUSEHOLD_ENTER instead? or is there any other method?