Welcome to
Mod The Sims
Online: 1219
News:
Have an account? Sign in:
pass:
If you don't have an account, why not sign up now? It's free!
Other sites: SimsWiki
Reply  Replies: 18 (Who?), Viewed: 27405 times.
Search this Thread
Old 26th Sep 2014, 11:25 PM DefaultFor absolute beginners with no clue: Making a simple script that gets the town's population. #1
mgomez
Original Poster

Lab Assistant

Join Date: Apr 2014
Posts: 154
Thanks: 7320 in 132 Posts
10 Achievements


So, my last tutorial might have been a little complex. In lieu of this, I decided to write something a little more introductory to The Sims 4's scripting framework. Today, we're going to make a "Get Town Population" mod (for reference, you can see the full source code and mod here). Hopefully, when we're done with this tutorial, you can begin to understand how the framework.. uhm... works.

The main purpose of this tutorial is to accustom you to finding things within The Sims 4's own Python code. It's a giant web of objects, classes, and functions, so it's easy to get lost. This is the process I normally go through when finding different things I need.

What You'll Need
  • The Sims 4's core Python modules.
  • A slight idea of how Python works.

So, now, we're going to have take a peek at the Python modules provided in The Sims 4 (you should be looking at PY files, not PYO files). You can find helpful ways to get your hands on these core modules here and here. "base", "core", and "simulation" are each labels to module collections that are used in the game's intelligence engine. We're going to concern ourselves with "core" and "simulation". As I've noticed, "base" contains a very large amount of library modules useful for certain micro-operations within the game's code, but aren't necessarily things we will often have to look at. "core" and "simulation" are most important for modding the game.

To successfully complete our little mod here, we need a command that you can type. When it's typed, it will reveal the population of all the sims in your game. Now that we know our goal, we can proceed to hunt for the things we need.

Let's have a look at "core" first. Within the core collection, you'll find "google", "shared_commands", and "sims4". Within sims4, you'll find several other libraries related to the handling of modder-created Python modules, algorithms essential for the functioning of the game, and key aspects found within The Sims 4's user interface. This is probably the best place to look if we want to find out where there is a function in the game that allows us to register new cheat console (Ctrl+Shift+C) commands. And speak of the devil! There's a "commands.py" file in there! Let's have a look...

It seems there's a function there called Command:

Code:
def Command(*aliases, command_type=CommandType.DebugOnly, pack=None):


Great! That would give us the first line of code that we need! Let's start our script!

Code:
import sims4.commands


It is important that we import the module or else we will end up with errors because our own module will not recognize the things we declare.

Now, it's time to insert the call to sims4.commands.Command() as a decorator. Whatever function we define directly below the decorator will be its "subject" (the decorator will be applied to that function). But wait! Before we register the command, we need to be sure that we've nailed down the command type. In "commands.py", there's an enumerated list of command types:

Code:
class CommandType(enum.Int, export=False):
__qualname__ = 'CommandType'
DebugOnly = 1
Automation = 2
Cheat = 4
Live = 5


OK, so we're going to register our command type as a Live command. Remember the definition of the Command() function we saw earlier? It's already passing a command_type for us, and we need to override that in our own code:

Code:
@sims4.commands.Command('getpopulation', command_type=sims4.commands.CommandType.Live)


Good! Now, it's time to declare a function right below this beautiful decorator that gives some output back, giving the player that types "getpopulation" the town's population. First, we need to find out what we should call to output to the cheat console, then we need to find out how to grab the town's population. This is where things get a little more tricky.

So, let's get that output function.... I think we should look in "commands.py" a little more.

Ah, found it!

Code:
class CheatOutput(Output):
__qualname__ = 'CheatOutput'

def __call__(self, s):
cheat_output(s, self._context)


The "__call__" function tells us that we only need to write a string when calling the function. Now, let's focus on getting the town's population.

For the town population and all that other jazz, we need to get out of the core module collection and dive into "simulation". In the "sims" folder, there's a file called "sim_info_manager.py". That looks like a good place to start.

There's a class in that file called "SimInfoManager". It seems to hold the details of every sim. If we could count all of the sims in its list, then that will give us the population of all sims. While looking through the rest of the code in TS4's repository, I'm noticing a pattern: sim_info_manager is an object in services. Its attachment to services is found in __init__.py inside the services folder. More importantly, it has a function that's called sometimes inside of the code files called get_all(), which returns a list element. That seems to have solved our mystery!

OK, so let's recap a little. Here's our code so far:

Code:
import sims4.commands

@sims4.commands.Command('getpopulation', command_type=sims4.commands.CommandType.Live)


All this searching and we've only written two lines of code! It's getting late and you're yawning, we've looked through all of TS4's code, and all we managed to finish is two lines! That's no problem, though, since we've found everything we need now to finish the mod!

We'll start by importing services (which will also import its __init__.py) at the top where we imported sims4.commands:

Code:
import services


Now, we need to go under the decorator and declare a function right there:

Code:
def getpop(_connection=None):


Don't worry about the _connection variable. I ripped that off of the maslow mod that EA wrote, and that's just become a habit now. You can call it fluffy_kittens if you'd like.

So, now that we know how to output to the cheat console (by calling CheatOutput), we should define a variable that calls it:

Code:
    output = sims4.commands.CheatOutput(_connection)


Now, all we have to do to output a string is to type output('blablabla'). But we don't want to output just 'blablabla'. We want to output the town's population. This is where our search for sim_info_manager in services pays off.

Code:
    output('Your town\'s population is {}'.format(len(services.sim_info_manager().get_all())))


You see, by measuring the length of the list we get when we call get_all() in services.sim_info_manager(), we get the count of all the sims in the universe! We've finished our mod!

This is what our final code looks like:

Code:
import services
import sims4.commands

@sims4.commands.Command('getpopulation', command_type=sims4.commands.CommandType.Live)
def getpop(_connection=None):
	output = sims4.commands.CheatOutput(_connection)
	output('Your town\'s population is {}'.format(len(services.sim_info_manager().get_all())))


Short, useful, succinct, elegant, and tastier than the cordon bleu I stole from my wife yesterday. This is the result of that code.



HINT: If you don't have Notepad++, GET IT. It's very useful for finding things within files. For example, I can use the "Find in Files" feature to search the entire Sims 4 repository for instances in which a particular function (like get_all() in this example) is called.
Last edited by justJones : 7th May 2018 at 12:05 AM.
7 users say thanks for this.
[ Click here to view a longer list ]
Old 26th Sep 2014, 11:30 PM #2
melbrewer367
Instructor

Join Date: Sep 2011
Posts: 619
Thanks: 4699 in 63 Posts
9 Achievements


This is awesome! I'm definitely going to go through all of this later! I've been wanting to learn how to do some of these things so that I could do my own tuning and things to customize the game for me but I had no idea of where to even start. Thank you! I'll be back later if I have questions!
Old 27th Sep 2014, 12:00 AM #3
mgomez
Original Poster

Lab Assistant

Join Date: Apr 2014
Posts: 154
Thanks: 7320 in 132 Posts
10 Achievements


Quote:
Originally Posted by melbrewer367
This is awesome! I'm definitely going to go through all of this later! I've been wanting to learn how to do some of these things so that I could do my own tuning and things to customize the game for me but I had no idea of where to even start. Thank you! I'll be back later if I have questions!


For tuning, I'd imagine you would have to deal with the game's ".package" files. Scripting strictly adds more functions into the game using by latching modules onto the game's code. One good way to think of it is that tuning modifies values in the game and adds tangible things to it (like objects, food, etc.) and scripting is more useful to modify how the game itself runs (i.e. my relationship decay mod)
Old 27th Sep 2014, 12:39 AM #4
melbrewer367
Instructor

Join Date: Sep 2011
Posts: 619
Thanks: 4699 in 63 Posts
9 Achievements


Quote:
Originally Posted by mgomez
For tuning, I'd imagine you would have to deal with the game's ".package" files. Scripting strictly adds more functions into the game using by latching modules onto the game's code. One good way to think of it is that tuning modifies values in the game and adds tangible things to it (like objects, food, etc.) and scripting is more useful to modify how the game itself runs (i.e. my relationship decay mod)


Right. Thank you for clarifying the difference. The scripting is what I meant like your relationship decay mod you mentioned (which I downloaded the same day you posted it!).
Old 27th Sep 2014, 1:32 AM #5
mgomez
Original Poster

Lab Assistant

Join Date: Apr 2014
Posts: 154
Thanks: 7320 in 132 Posts
10 Achievements


Quote:
Originally Posted by melbrewer367
Right. Thank you for clarifying the difference. The scripting is what I meant like your relationship decay mod you mentioned (which I downloaded the same day you posted it!).


I certainly hope you have the latest version. Earlier version had a glaring bug because of some of the game mechanisms (some of which I might explain in another tutorial, when I find the time to stop banging my head against my desk).

I'm happy to help if you ever need some guidance in making mods. The process becomes a bit intuitive once you get the hang of how the game's nuts and bolts clunk together.
Old 3rd Oct 2014, 5:31 PM #6
Panndora
Test Subject

Join Date: Sep 2014
Posts: 1


I did everything you said but it doesn't work
Maybe I saved it wrong or something?
I must admit I've never used python, more used to Java.
I really want to start making mods so if you could be kind enough to help me out a bit I would be thankful for ever :D
Old 15th Oct 2014, 6:33 AM DefaultCompilation #7
Zaruski
Test Subject

Join Date: Oct 2014
Posts: 1


Hi mgomez!

Thank you for the tutorial. It is very helpful, short and to the point. Great to get somebody started!

But I am trying to compile my own script and it doesn't seem to work. I think I am not creating the .py file in the correct directory. It complains about modules not existing.

What directory (core, base, or simulation) should the script be created in in order for compilation to finish successfully?

Thanks!

Zar
Old 21st Oct 2014, 6:31 PM #8
despotzapper
Test Subject

Join Date: Mar 2009
Posts: 14


Could this process be used to mod NPC behaviors as a whole town? say like getting sims to go to work,and have and raise their own offspring?
Old 26th Oct 2014, 10:11 PM #9
crcchelsea
Test Subject

Join Date: Aug 2012
Posts: 3


So I've followed the other tutorials to decompile the pyo files and I'm running into an issue that has been asked before, but I haven't found any answers. When I call TheHologramMan’s unpyc3.py with a .pco file I ALWAYS get back the "USAGE: C:\Python33\unpyc3.py <filename.pyc>" message, as if I left off the file parameter...but I didn't. This happens even when I don't use the batch script - to test, I copied abc.pyo from base.zip to my Python dir (Python33) so that it's in the same folder as unpyc3.py, cd'd to that folder (even though it's in my PATH so I shouldn't have to), and called exactly this: unpyc3.py abc.pyo

It didn't work. Suggestions?
Old 7th Apr 2015, 10:36 PM #10
recursor94
Test Subject

Join Date: May 2013
Posts: 12


In this tutorial, you've said that we should be looking at .py files, and not .pyo files. However, the decompiler bat script only generates .pyo files.
Old 9th Sep 2015, 11:20 AM #11
Lynire
Test Subject

Join Date: Jun 2013
Posts: 30
Thanks: 1736 in 70 Posts
8 Achievements


After you get your .py file, in order to get it to work you need to make a .pyo file and put that along with your .py file into a zip file. The zip file is what goes into your Sims 4 Mods folder. To make a .pyo file, you can create a batch file named something like PythonOptimizeCompile.bat and put this in it:

Code:

@echo off
if %1x==x goto usage
%~d1
cd "%~p1"
echo on
python -O -c "import py_compile;py_compile.compile('%~n1%~x1','%~n1.pyo')"
@echo off
goto end
:usage
echo drag+drop the .py file onto this to compile to a .pyo file.
:end
pause


If you have more than one Python version installed and the version found by your path is not the version used by Sims 4 (3.3.5) then you will need to put the full path in for the python command rather than just "python" or change your path variable.

Then when you drag+drop your .py file onto that batch file it will create a .pyo file with the same base name as the .py file in the same folder, provided python is in your path. This applies to Windows.
Last edited by Lynire : 17th Dec 2015 at 5:00 PM.
Old 31st Jan 2016, 12:14 PM #12
Inge Jones
One horse disagreer of the Apocalypse

Join Date: Sep 2004
Posts: 11,222
Thanks: 9135 in 18 Posts
20 Achievements


Testing this in game, I got "Unable to execute command". Obviously there is no clue where it went wrong. It appeared to compile ok.

"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Old 31st Jan 2016, 12:24 PM #13
plasticbox
Pettifogging Legalist!



Join Date: Oct 2005
Posts: 11,593
Thanks: 378265 in 1050 Posts
46 Achievements


Do you see anything in lastException.txt? "Unable to Execute" is a good sign, it means that your script is basically working and the game only does not understand what you're trying to tell it.

Maybe this thread helps: http://forums.thesims.com/en_US/dis...-the-game-files -- this is when I made my first script attempt, and SGEugi basically walked me through it step by step. I believe about 50% of all things one could possibly do wrong are covered in that thread

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.
Old 31st Jan 2016, 1:21 PM #14
Inge Jones
One horse disagreer of the Apocalypse

Join Date: Sep 2004
Posts: 11,222
Thanks: 9135 in 18 Posts
20 Achievements


I think my compiling is wrong as the .py works ok as a loose file, just the .pyo not working now.

"You can do refraction by raymarching through the depth buffer" (c. Reddeyfish 2017)
Old 15th Nov 2016, 12:52 AM #15
ZeroG667
Lab Assistant

Join Date: Feb 2016
Posts: 72
Thanks: 129 in 5 Posts
4 Achievements


Awesome. But what is the _connection=None argument for?(I know you said i don't have to worry about it... But it's intriguing) And where is the get_all() command declared?
Old 15th Nov 2016, 10:17 PM #16
crissylp89
Test Subject

Join Date: Sep 2016
Posts: 10


So this is a fairly old thread and may not even be the right place to ask but ... I cannot even get past downloading python :/ I go through the installation wizard and it says it can't be completed because there is no software to install. I was hoping someone here had heard of this problem before, or is it strictly something I need to contact the python website about. Thanks
Old 30th Jan 2017, 3:32 PM #17
jerrycnh
Test Subject

Join Date: Jan 2017
Posts: 40
Thanks: 236 in 7 Posts
5 Achievements


Can this method override an existing pyo in the game, or can it only supplement?
Old 18th Mar 2017, 7:45 PM #18
Daemigod
Test Subject

Join Date: Feb 2017
Posts: 2


Hey I tried this tutorial out for myself and it worked after a bit of troubleshooting and diffing.

What I noticed is that in your final code piece listed at the bottom of your original post you have:

Code:
def getpop(_connection=None)


but in your source code for your actual mod you just have:

Code:
def getpop(_connection)


I compiled and tested both versions of this with the =None and without, and found that just having def getpop(_connection), the command would work in game.

Was setting it equal to None a typo?

(p.s. I'm a total noob at python, hopefully this might help someone else that's running thru this tutorial and having problems at the end)
Old 25th Oct 2017, 8:18 PM #19
elimegreeneyed
Test Subject

Join Date: Sep 2014
Posts: 2


I've done all I have to do to create the core folder, but inside I've found only .pyo files, none .py files. Where I go wrong?
Reply


Section jump:


Powered by MariaDB Some icons by http://dryicons.com.