- Site Map >
- Modding and Creation >
- Sims 4 Creation >
- Modding Discussion >
- Script Override using an Injector - failed attempt - send help
- Site Map >
- Modding and Creation >
- Sims 4 Creation >
- Modding Discussion >
- Script Override using an Injector - failed attempt - send help
Replies: 8 (Who?), Viewed: 2745 times.
#1
19th Jul 2018 at 5:33 AM
Posts: 13
Thanks: 369 in 1 Posts
Script Override using an Injector - failed attempt - send help
Hi there!I've just tapped into script modding veeery briefly like today. This is worse than xml files. I originally planned on doing something more elaborate, but I've given up on that already. What I want to do instead now is a simple override. Google says, things like that are done by using an “injector”, so I found THIS post.
The plan is to change the default baby skin tone that is used if the inherited skin tone is unnatural/custom content to something more visible than the original light. I would prefer to add my own skins, but I wouldn't even know where to start with that.
So, trying to follow Deaderpool's instructions I came up with this:
Code:
import sims.baby.baby_tuning import sims.sim_info import injector @injector.inject_to(sims.baby.baby_tuning.BabyTuning, "get_baby_skin_tone_enum") def inject_baby_skin(original, sim_info): original(sim_info) if sim_info.is_baby: skin_tone_id = sim_info.skin_tone for (skin_enum, tone_ids) in BabyTuning.BABY_SKIN_TONE_TO_CAS_SKIN_TONE.items(): while skin_tone_id in tone_ids: return skin_enum return BabySkinTone.ALIEN_PURPLE return BabySkinTone.ADULT_SIM
I used THIS tutorial to set up my workspace and compile the script. I added the injector.py from Deaderpool's post.
Sadly, all this does is throw an error and have bassinet and baby vanish on delivery. And since I don't really know what I'm doing in the first place, I have now idea how to fix this. For my inapt eyes this looks just like the example in the post I mentioned.
Creating script mods is such a broad and complicated topic and tutorials seem to be really rare, so if anyone has mercy on me, any help would be appreciated.
Thanks in advance!
Advertisement
#2
19th Jul 2018 at 6:26 AM
Posts: 2,671
Thanks: 62729 in 190 Posts
You are getting errors as some of those values are not fully referenced so Python can't find them. The original code had direct access to the BabyTuning and the BabySkinTone classes, but your script doesn't as it's running from a different module. I'd suggest changing the code to use the from style of import, or you can just add sims.baby.baby_tuning in front of the references to BabyTuning and BabySkinTone as you did in the injector decorator. Using the from import style, it would look like
Some additional notes:
ETA - I forgot to mention, I changed the "while" in the script to an "if". The script decompilers frequently will replace something that should be an "if" with a "while" instead and this is one of those cases. Although it doesn't hurt in this particular script as when the while is true it returns from the function, in some it will cause an infinite loop if a while is used incorrectly. Of course, not all whiles in the scripts are supposed to be ifs, some actually should be whiles - all you can do is read the code and evaluate what it should be doing whenever you run into a while loop. You can always ask here if you're in doubt about one you run across.
Code:
from sims.baby.baby_tuning import BabyTuning, BabySkinTone import injector @injector.inject_to(BabyTuning, "get_baby_skin_tone_enum") def inject_baby_skin(original, sim_info): if sim_info.is_baby: skin_tone_id = sim_info.skin_tone for (skin_enum, tone_ids) in BabyTuning.BABY_SKIN_TONE_TO_CAS_SKIN_TONE.items(): if skin_tone_id in tone_ids: return skin_enum return BabySkinTone.ALIEN_PURPLE return BabySkinTone.ADULT_SIM
Some additional notes:
- You don't need to import sims.sim_info, that's only necessary if you're using something directly from the sim_info module; however, you're getting a direct reference to a sim_info object, so you can access parts of that object without any need to import.
- You don't need to call the original, since you're completely overriding what it is doing there is no need for it to run before your code. You generally only need to call the original if you just want to do something else before or after it runs.
- With the from import method shown above you can leave out the sims.baby.baby_tuning before BabyTuning in the injector line.
- I don't have a couple that requires that default skin tone in my game, so I can't really test that your code works as you want - but it won't throw errors with these changes.
- Finally, one helpful hint - it's easiest to get the code working without bothering to compile it. You can run .py scripts directly without compiling them by placing them into a sub-sub-folder named "Scripts", for example, I use the folder "Mods/Script Testing/Scripts" for a lot of simple script development. For bigger projects, I'll create a whole new subfolder like "Mods/NAMEofMOD/Scripts"
ETA - I forgot to mention, I changed the "while" in the script to an "if". The script decompilers frequently will replace something that should be an "if" with a "while" instead and this is one of those cases. Although it doesn't hurt in this particular script as when the while is true it returns from the function, in some it will cause an infinite loop if a while is used incorrectly. Of course, not all whiles in the scripts are supposed to be ifs, some actually should be whiles - all you can do is read the code and evaluate what it should be doing whenever you run into a while loop. You can always ask here if you're in doubt about one you run across.
#3
20th Jul 2018 at 1:01 AM
Posts: 13
Thanks: 369 in 1 Posts
Thank you so much, scumbumbo! I just tested your changed script and it works just as planned. And what a nice and detailed answer, I understand some parts a little better now. Others not yet, but this gives me hope.
I wasn't sure how to identify what exactly to import for the script to run properly. At one point I even imported every file with a name that was similar enough to any of the words in my script.
And I was wondering what to change if I want to do a simple override instead of adding something to a script. The post didn't cover that and I didn't expect it to be as easy as just omitting the original part.
Using sub-subfolders works like a charm as well, that will make testing a lot faster.
I'll keep the while-if part in mind.
I really appreciate the time you spend to help me out here, this may seem minor to a veteran, but it's a big step forward for me (I consider myself being able to override stuff now, ha).
So all my thanks to you!
The next part almost borders on contemplating the meaning of life, now that I look at it, so I wouldn't really consider it an actual follow up question. But if someone is interested in a mod like this, is looking for a challenge or simply bored, feel free to add your two cents.
This is a mere workaround and for my current legacy it's good enough, but not exactly something to share with the world yet (I hope at least this thread can be helpful to someone though). Originally I wanted to add more baby skin tones for a more accurate representation of the parents, but I don't even understand how or where the game determines which skin to use. I've been looking at the files again and again before opting for the workaround, but it feels like I can see only half of what's going on.
For example in the script part I tried to overwrite: It checks for the skin_tone_id if it is in tone_ids. There is no mention whatsoever of any kind of id list named like that in any other file. So how does it look like? How are the ids calculated? There is a list of all the possible baby skin tones with numbers at the start of the script, but for ids they are pretty short, so that's probably not it. Or is it as easy as a number from 0 to 15 hidden somewhere inside the cas part of an adult skin tone? Is it hardcoded to the instance ids of the original EA skin tones?
I don't get it. Or maybe I just don't get it because I don't get scripts yet. I don't know. And I'm not even able to ask the right questions to somehow work my way up there. That's the actual frustrating part. Oh well. I'll be looking at stuff some more, from scripts to object tuning.
Until then, have a nice and successful day everyone!
I wasn't sure how to identify what exactly to import for the script to run properly. At one point I even imported every file with a name that was similar enough to any of the words in my script.
And I was wondering what to change if I want to do a simple override instead of adding something to a script. The post didn't cover that and I didn't expect it to be as easy as just omitting the original part.
Using sub-subfolders works like a charm as well, that will make testing a lot faster.
I'll keep the while-if part in mind.
I really appreciate the time you spend to help me out here, this may seem minor to a veteran, but it's a big step forward for me (I consider myself being able to override stuff now, ha).
So all my thanks to you!
The next part almost borders on contemplating the meaning of life, now that I look at it, so I wouldn't really consider it an actual follow up question. But if someone is interested in a mod like this, is looking for a challenge or simply bored, feel free to add your two cents.
This is a mere workaround and for my current legacy it's good enough, but not exactly something to share with the world yet (I hope at least this thread can be helpful to someone though). Originally I wanted to add more baby skin tones for a more accurate representation of the parents, but I don't even understand how or where the game determines which skin to use. I've been looking at the files again and again before opting for the workaround, but it feels like I can see only half of what's going on.
For example in the script part I tried to overwrite: It checks for the skin_tone_id if it is in tone_ids. There is no mention whatsoever of any kind of id list named like that in any other file. So how does it look like? How are the ids calculated? There is a list of all the possible baby skin tones with numbers at the start of the script, but for ids they are pretty short, so that's probably not it. Or is it as easy as a number from 0 to 15 hidden somewhere inside the cas part of an adult skin tone? Is it hardcoded to the instance ids of the original EA skin tones?
I don't get it. Or maybe I just don't get it because I don't get scripts yet. I don't know. And I'm not even able to ask the right questions to somehow work my way up there. That's the actual frustrating part. Oh well. I'll be looking at stuff some more, from scripts to object tuning.
Until then, have a nice and successful day everyone!
#4
20th Jul 2018 at 7:46 AM
Last edited by scumbumbo : 20th Jul 2018 at 7:53 AM.
Reason: clarified the staticmethod note
Posts: 2,671
Thanks: 62729 in 190 Posts
Just a quick response at the moment, I'll touch on the latter part of your "meaning of life" question later when I have some more time.
I pretty much started out in much the same way - anything that mentioned sim_info I figured I had to import the sim_info module. But what it boils down to makes a lot more sense -- you need to import anything that you don't otherwise have a reference to. Since an instance of a sim_info class was passed to your function, you can access anything from that sim_info without any other reference required. But when injecting or overriding a script from the game's scripts they may reference things that are not in your module. These may be in the original module, like in the case of the BabySkinTone enum class from your script - those are in the same method as that function had originally been defined in so it didn't need a reference to those. But since your module doesn't see that enum, you need to have a reference to it.
It could also be from another module entirely, in which case look at the imports at the top of the file you are modifying the function from and look to see what they are importing. If that BabySkinTone had been from another method, then somewhere at the top of that file they would've had a "from whatever import BabySkinTone" in the game script, so that would tell you what method it had come from and that you would need to do the same import in your script.
It'll make perfect sense in time, and when you forget to import something it should throw an exception into the LastException file that should make it clear that you are referencing something incorrectly. For example, if I tried to create a notification box from a script, but forgot to import the notification class (UiDialogNotification) from the module it is from (ui_dialog_notification), then the exception would tell me (along with the line of my code where the error occurred).
That's just one way to do it, there's a bit more overhead involved with that method as it uses the injector (which internally is just sort of doing a fancy override while saving a copy of the original function) but that overhead is extremely minimal (maybe 1kb extra code and a few nanoseconds of extra cpu time). But the other way is simpler and more direct and that is to just directly overwrite the method from the original module. For example:
If it looks a lot like just overwriting a variable that's because it kind of is. It works because everything in Python is an object even a class or method in another module, so we can nearly always write over it and hopefully do great things. Or possibly causing the game to go haywire, but you get used to that and learn to fix it (hopefully before releasing the mod, lol).
Hmm, did I mention that you can reload a uncompiled script that is in a folder like that without having to exit and restart the game? Considering keeping that a secret...
Nah... It's not really a secret, just probably some newer modders don't know because it was mentioned so long ago here. I keep a copy of that reload script in my script testing folder and it's probably saved me at least 100 hours of exit/reload time when playing with "what can I screw up next" ideas.
Quote: Originally posted by godofallbeauties
I wasn't sure how to identify what exactly to import for the script to run properly. At one point I even imported every file with a name that was similar enough to any of the words in my script. |
I pretty much started out in much the same way - anything that mentioned sim_info I figured I had to import the sim_info module. But what it boils down to makes a lot more sense -- you need to import anything that you don't otherwise have a reference to. Since an instance of a sim_info class was passed to your function, you can access anything from that sim_info without any other reference required. But when injecting or overriding a script from the game's scripts they may reference things that are not in your module. These may be in the original module, like in the case of the BabySkinTone enum class from your script - those are in the same method as that function had originally been defined in so it didn't need a reference to those. But since your module doesn't see that enum, you need to have a reference to it.
It could also be from another module entirely, in which case look at the imports at the top of the file you are modifying the function from and look to see what they are importing. If that BabySkinTone had been from another method, then somewhere at the top of that file they would've had a "from whatever import BabySkinTone" in the game script, so that would tell you what method it had come from and that you would need to do the same import in your script.
It'll make perfect sense in time, and when you forget to import something it should throw an exception into the LastException file that should make it clear that you are referencing something incorrectly. For example, if I tried to create a notification box from a script, but forgot to import the notification class (UiDialogNotification) from the module it is from (ui_dialog_notification), then the exception would tell me (along with the line of my code where the error occurred).
Code:
NameError: global name 'UiDialogNotification' is not defined
Quote:
And I was wondering what to change if I want to do a simple override instead of adding something to a script. The post didn't cover that and I didn't expect it to be as easy as just omitting the original part. |
That's just one way to do it, there's a bit more overhead involved with that method as it uses the injector (which internally is just sort of doing a fancy override while saving a copy of the original function) but that overhead is extremely minimal (maybe 1kb extra code and a few nanoseconds of extra cpu time). But the other way is simpler and more direct and that is to just directly overwrite the method from the original module. For example:
Code:
from sims.baby.baby_tuning import BabyTuning, BabySkinTone # define your override function with no inject decorator # and don't add "original" to the argument list # *** and for this particular function DO include the staticmethod decorator from # *** the original so it gets defined as a staticmethod # *** (otherwise it would expect a "self" argument) @staticmethod def inject_baby_skin(sim_info): if sim_info.is_baby: etc. etc. etc. # and overwrite the original with yours BabyTuning.get_baby_skin_tone_enum = inject_baby_skin
If it looks a lot like just overwriting a variable that's because it kind of is. It works because everything in Python is an object even a class or method in another module, so we can nearly always write over it and hopefully do great things. Or possibly causing the game to go haywire, but you get used to that and learn to fix it (hopefully before releasing the mod, lol).
Quote:
Using sub-subfolders works like a charm as well, that will make testing a lot faster. |
Hmm, did I mention that you can reload a uncompiled script that is in a folder like that without having to exit and restart the game? Considering keeping that a secret...
Nah... It's not really a secret, just probably some newer modders don't know because it was mentioned so long ago here. I keep a copy of that reload script in my script testing folder and it's probably saved me at least 100 hours of exit/reload time when playing with "what can I screw up next" ideas.
#5
20th Jul 2018 at 10:04 AM
Posts: 2,671
Thanks: 62729 in 190 Posts
Quote: Originally posted by godofallbeauties
Originally I wanted to add more baby skin tones for a more accurate representation of the parents, but I don't even understand how or where the game determines which skin to use. I've been looking at the files again and again before opting for the workaround, but it feels like I can see only half of what's going on. For example in the script part I tried to overwrite: It checks for the skin_tone_id if it is in tone_ids. There is no mention whatsoever of any kind of id list named like that in any other file. So how does it look like? How are the ids calculated? There is a list of all the possible baby skin tones with numbers at the start of the script, but for ids they are pretty short, so that's probably not it. Or is it as easy as a number from 0 to 15 hidden somewhere inside the cas part of an adult skin tone? Is it hardcoded to the instance ids of the original EA skin tones? |
Ok, back from what I needed to get done and able to type up a reply to this part, where does the tone_ids come from? The short answer is that it comes from the tuning. The long answers, well I have a feeling this is going to get long enough for a...
First, you're going to want to get the Sims Log Enabler if you don't already have that. Or you can write your own logging functions if you'd rather not run that mod. I use SLE all the time and have the option set in the code to have it start logging automatically myself so I can always look at everything that's getting logged while the game runs, but THIS DOES SLOW DOWN THE GAME CONSIDERABLY - in particular the load time at the start, not so much while the game is running.
Ok, assuming you are using SLE, we'll add a Logger class to the code so we can dump the values of any variables we don't know the contents of into a log file, like so
Code:
from sims.baby.baby_tuning import BabyTuning, BabySkinTone import sims4.log logger = sims4.log.Logger('BabyTuningOverride') @staticmethod def override_baby_skin(sim_info): logger.debug('override_baby_skin({})', sim_info) if sim_info.is_baby: skin_tone_id = sim_info.skin_tone logger.debug(' skin_tone_id={}', skin_tone_id) logger.debug(' BABY_SKIN_TONE_TO_CAS_SKIN_TONE={}', BabyTuning.BABY_SKIN_TONE_TO_CAS_SKIN_TONE) for (skin_enum, tone_ids) in BabyTuning.BABY_SKIN_TONE_TO_CAS_SKIN_TONE.items(): logger.debug(' skin_enum={}, tone_ids={}', skin_enum, tone_ids) if skin_tone_id in tone_ids: logger.debug(' found a match, returning skin_enum={}', skin_enum) return skin_enum logger.debug(' no match, so returning ALIEN_PURPLE') return BabySkinTone.ALIEN_PURPLE logger.debug(' not a baby, so returning ADULT_SIM') return BabySkinTone.ADULT_SIM BabyTuning.get_baby_skin_tone_enum = override_baby_skin
So one of the first things you'll note in the debug log in the Sims Log Enabler folder is that the value of BABY_SKIN_TONE_TO_CAS_SKIN_TONE changed from what it was defined as at the top of the class, to something with a lot more information in it.
Code:
[DEBUG] BABY_SKIN_TONE_TO_CAS_SKIN_TONE=_sims4_collections.frozendict({<BabySkinTone.LIGHT = 0>: (12122, 12140, 21829, 30679, 44997, 14400, 12137, 148818, 148822, 191487, 44999), <BabySkinTone.MEDIUM = 1>: (12139, 30714, 45003, 45000, 30678, 148819, 148821, 191485, 191455), <BabySkinTone.DARK = 2>: (12136, 12138, 31444, 45004, 148820, 148817, 191480, 191901, 191902, 191909, 191995, 191898, 191488), <BabySkinTone.BLUE = 3>: (45001,), <BabySkinTone.GREEN = 4>: (45002,), <BabySkinTone.RED = 5>: (146066,), <BabySkinTone.ALIEN_BLUE = 6>: (84187,), <BabySkinTone.ALIEN_BLUE_LIGHT = 7>: (84185,), <BabySkinTone.ALIEN_GREEN = 8>: (84188,), <BabySkinTone.ALIEN_GREEN_LIGHT = 9>: (84186,), <BabySkinTone.ALIEN_PURPLE = 10>: (84177,), <BabySkinTone.ALIEN_PURPLE_LIGHT = 11>: (84184,), <BabySkinTone.ALIEN_TEAL = 12>: (84189,), <BabySkinTone.ALIEN_TEAL_LIGHT = 13>: (84190,), <BabySkinTone.ALIEN_WHITE = 14>: (84191,)})
So how did all that information get into that variable from the definition line. The answer lies in the fact that it's a TunableMapping - that's right, it's coming from the XML tuning, in this case the module tuning for the baby_tuning module. If you look in that XML file (it'll be named S4_03B33DDF_00000000_703619063DA7E805.xml if you use my XML Extractor program, not sure the filename if you use Sims 4 Studio, but either one you use just search for the XML for 'baby_tuning' and you should find it), you'll see a bunch of lines that map a "key" which is the enum value from that BabySkinTone enum in the file to a list of "values". Those values are just numbers, they actually refer to the instance ID of the TONE resources from the game's packages. I'm not at all sure how those TONE resources are setup, someone else may be able to help you with that if you want to edit those, I just mention because otherwise those numbers might make no sense for you.
The main point is that we can see that from the debug line above that the key <BabySkinTone.LIGHT> is mapped to the list (actually a tuple) of (12122, 12140, 21829, 30679, 44997, 14400, 12137, 148818, 148822, 191487, 44999). This matches the list of values from the LIGHT key from that XML tuning file.
Code:
<U> <E n="key">LIGHT</E> <L n="value"> <T>12122</T> <T>12140</T> <T>21829</T> <T>30679</T> <T>44997</T> <T>14400</T> <T>12137</T> <T>148818</T> <T>148822</T> <T>191487</T> <T>44999</T> </L> </U>
So that's where those values come from. Actually editing those TONE resources, again, is beyond me but I'm sure someone knows how that works.
So that tuning gets converted to a frozendict (an immutable type of dict, basically it works the same as a dict but it can't be changed once it's defined - it can be REPLACED with a new dict, but it can't have any of the values already stored in it changed). Anyway, long parenthetical remark so I'll kind of repeat that frozendict just stores the values from that XML file and maps a bunch of keys to a set of values. So we can lookup BabySkinTone.ALIEN_TEAL in that dict and get the value 84189 out. Or, we can loop through all the values in the dict, which is what that for line is doing. The items() method of the dict class returns each of the items in the dict as a key / value pair. This is why there are two variables at the start of the for statement, the first variable skin_enum gets the key, the second tone_ids gets the value. You can see it stepping through that dict when you read further into the debug output.
If the game finds a match where the skin_tone_id loaded from that sim_info is in the tone_ids list for a specific enum value, it then returns that enum value. If it doesn't find one then it returns your ALIEN_PURPLE. In the test I ran, there were triplets, one had skin_tone_id 44997, one was 44999 and the last one was 45003. All of these had matches, two from skin tone light, and the last from skin tone medium. Your results when you run the logging will, of course, vary.
So, not quite finally, I'm not sure how exactly it picks a baby's skin tone from the combination of the parents, but I think I've done one better in showing how you can debug the values in the code to start figuring that out for yourself which is far more valuable. If you do have any questions just yell.
Finally, one last note about injecting a method vs. overriding it and why it can matter. If you make some changes to the logging and try to reload the script it will work just fine for an override. But for something where you're using inject and calling the original, it will have dramatically different results because you'll actually be injecting into something that's already injected, so it will run the original twice and one copy of each of your injections. If you reload again, it will run THREE times, and so forth. A lot of times that won't matter much, but sometimes it will produce very odd results, so something you should be aware of.
Finally squared - yes, Python can be a strange language sometimes The fact that a function can return multiple values (not even limited to two like the items() iterator in this example), you can overwrite functions with other functions, objects can be immutable so they can't be changed (but you can still replace them with a completely new object if you need to change it)... it can get quite confusing sometimes, but keep at it and it all starts to make sense after a while and you'll be "monkey patching" code from the scripts with the rest of us. And again, any questions and just yell. There's several script authors who hang out at Deaderpool's Discord server, I'm on there pretty often as well as Deaderpool, Turbodriver and several others, and some folks just starting into Python scripting like yourself as well.
#6
22nd Jul 2018 at 9:35 AM
Posts: 13
Thanks: 369 in 1 Posts
I don't know how much more I should write in here, as the original question has been answered already and I don't want this to turn into spam, but I at least want to say thank you again.
Honestly, this thread is such a treasure. I've gotten a grasp on things already I never though I'd understand and after consulting with Google I have a feeling this may help others as well. It was unexpectedly fun using the SLE to look at different things and digging around the game files, too, I even made me a little diagram to better understand how everything ties together. While doing so I also managed to hit several walls already, (like how none of the skin tone ids match with any number, instance or otherwise, present in the TONE files, sadly, that would've made things a lot easier...) but I'm working on it.
When I posted this I really wasn't expecting someone to put this grade of detail and effort into answering me. And one of the veteran modders of the community no less. I've saved all the info faster than crashing my game with all the new playthings I got. So know that your time writing up those elaborate responses was not spend in vain and that my gratitude will be yours forever. (Or at least until I come to hate everything because nothing ever works. “Monkey Patching” is a great term, ha)
Thank you for your work!
Honestly, this thread is such a treasure. I've gotten a grasp on things already I never though I'd understand and after consulting with Google I have a feeling this may help others as well. It was unexpectedly fun using the SLE to look at different things and digging around the game files, too, I even made me a little diagram to better understand how everything ties together. While doing so I also managed to hit several walls already, (like how none of the skin tone ids match with any number, instance or otherwise, present in the TONE files, sadly, that would've made things a lot easier...) but I'm working on it.
When I posted this I really wasn't expecting someone to put this grade of detail and effort into answering me. And one of the veteran modders of the community no less. I've saved all the info faster than crashing my game with all the new playthings I got. So know that your time writing up those elaborate responses was not spend in vain and that my gratitude will be yours forever. (Or at least until I come to hate everything because nothing ever works. “Monkey Patching” is a great term, ha)
Thank you for your work!
#7
22nd Jul 2018 at 7:30 PM
Posts: 2,671
Thanks: 62729 in 190 Posts
Hmmm..... Try converting the skin tone ids from decimal to hexadecimal and then see what they match
The skin tone ids are the instance ids for the TONE resource, it's just in decimal whereas you are used to seeing instance IDs in hexadecimal.
#8
23rd Jul 2018 at 11:10 AM
Posts: 13
Thanks: 369 in 1 Posts
Quote: Originally posted by scumbumbo
Hmmm..... Try converting the skin tone ids from decimal to hexadecimal and then see what they match |
Mind blown!
How do you come up with all this stuff? Is it magic? A sixth sense? Divine afflatus? Whatever it is, this definitely removes a black box from my diagram once again. Nice!
#9
23rd Jul 2018 at 8:50 PM
*simsample cries*
This thread is a great example of why I love this site!
This thread is a great example of why I love this site!
I will choose a path that's clear- I will choose free will
-RUSH- -RADIO- -RADIO- -EON- -ARCHIVES-
Simpeople and Me Archive- 11Dots Archive- My Sims World Archive- Sims 1 Archive
Angel Classic Rock Mix!
-RUSH- -RADIO- -RADIO- -EON- -ARCHIVES-
Simpeople and Me Archive- 11Dots Archive- My Sims World Archive- Sims 1 Archive
Angel Classic Rock Mix!
Who Posted
|