MTS has all free content, all the time. Find out how YOU can help to keep it running. Tell me how...

Console –> PC: Karma Powers

by gamefreak130 Posted 22nd Sep 2021 at 2:15 AM - Updated 22nd Sep 2021 at 8:08 PM by gamefreak130
 
79 Comments / Replies (Who?) - 30 Feedback Posts, 48 Thanks Posts
Thanks are currently OFF: Show Thanks Posts for this thread
Page 1 of 4
Instructor
Original Poster
#2 Old 22nd Sep 2021 at 2:17 AM
Creating Custom Powers
If you have experience with script modding, then creating your own karma powers shouldn't be too difficult. You don't even need an instantiator!

This guide shows how to create a custom power from scratch using an example "Lightning Strike" power I've made. The final mod package is available on the main download page, and the full source for this project (as well as the main mod) is available here.

Getting Started
Start by opening up the main mod in S3PE and finding the S3SA resource named "Gamefreak130.WonderPowers". Right-click and select "Export DLL", then choose a location to save the file.

Next, follow the steps outlined here or here to set up a new script modding project in your IDE of choice. Once the project is set up, go to the references list and add a reference to the Gamefreak130.WonderPowers assembly you just exported. Your reference list should look similar to this:



Note:
Because this project directly references my karma powers mod, packages created using this tutorial will crash and/or otherwise break the game if the main mod is not installed. Make sure your users are aware of this!
Creating the Package
Set the C# project aside for a moment and create a new package in S3PE. In it, add the following items:
  • An _XML (0x0333406C) resource. The name/instance doesn't matter, but make a note of it, as it will be used later.
  • An S3SA (0x073FAA07) resource, whose name/instance matches that of your C# project.
  • One or more STBL (0x220557DA) resources for each language you plan to support. Again, the name/instance of these resources does not matter, except that the first two numbers of the instance hash must match the code of the language associated with that STBL. You can check out this tutorial for more info on STBLs and language codes.

The group for these and all other items in the package can simply be set to 0, unless you have a need for some other group value (e.g. when adding audio files).

Creating the Booter XML
In order to load powers into the game, the main mod relies on "Power Booters", which point to XML resources containing all the information the mod needs to add the power to the system. We'll start by filling out this power info XML so that later we can write the Booter that will point to it.

Start by right-clicking on the XML resource you created in the package, selecting "Text Editor", and copy-pasting the following into Notepad:

Code:
<?xml version="1.0" encoding="utf-8" ?>
<KarmaPowers>
  <Power>
    <ProductVersion>BaseGame</ProductVersion>
    <PowerName></PowerName>
    <IsBad></IsBad>
    <Cost></Cost>
    <EffectMethod></EffectMethod>
  </Power>
</KarmaPowers>


Then, for each power you want to add, copy the "<Power>" tags and everything in between them, and paste below the first set of "<Power>" tags but within the "<KarmaPowers>" tags. Then, in between each set of tags inside the "<Power>" tags you pasted, enter the following information:
  • ProductVersion: The expansion pack, if any, that must be installed for the power to become available. Internally, expansions start with "EP" and are numbered in the order they were originally released; valid entries include "BaseGame" if no expansions are required, "EP2" for Ambitions, and "EP11" for Into the Future.
  • PowerName: The name used to refer to the power internally. It doesn't really matter what it is as long as it's unique. Make a note of it, as it will be used later.
  • IsBad: True/False: Whether or not the power will appear as a "bad" power in-game, as opposed to a "good" one.
  • Cost: The karma points required to purchase the power. Cannot exceed 200.
  • EffectMethod: The static activation method the game will trigger when the karma power is activated. Leave this blank for now, we'll come back to it later.

Your XML should now look something like this:

Code:
<?xml version="1.0" encoding="utf-8" ?>
<KarmaPowers>
  <Power>
    <ProductVersion>BaseGame</ProductVersion>
    <PowerName></PowerName>
    <IsBad></IsBad>
    <Cost></Cost>
    <EffectMethod></EffectMethod>
  </Power>
  <Power>
    <ProductVersion>EP8</ProductVersion>
    <PowerName>LightningStrike</PowerName>
    <IsBad>True</IsBad>
    <Cost>20</Cost>
    <EffectMethod></EffectMethod>
  </Power>
</KarmaPowers>


Save the file and commit the changes.

Note:
While you cannot require multiple expansion packs for a power to become available, you can allow a power to support one of any number of expansions by creating duplicate entries in the XML, with each <ProductVersion> value set to a different expansion you want to support. An example of this is the "Lucky Find" power in the main mod, which will appear if either Pets or Supernatural is installed.
Adding Power Strings and Images
Next, let's add the images and strings that will appear in the karma power selection menu. For each power in your XML, add two IMAG (0x2F7D0004) resources to your package named "[PowerName]" and "[PowerName]_Preview" (minus the quotes), respectively, where "[PowerName]" is the name you gave to the power in the XML. Then, right-click each IMAG and select "Replace" to save a PNG file into the resource. The resource named "[PowerName]" is the icon that will appear in the power grid; a resolution of around 52 x 52 seems to work best. The other image is the preview that appear next to the power's title, cost, and description, and should be around 200 x 200, though feel free to experiment later if you feel it looks off.

Once the images are added, right-click on your STBL and select "Edit STBL". Click on the text box at the bottom of the screen and enter "UI/WonderMode/KarmaMenu:[PowerName]" (minus the quotes), where "[PowerName]" is the name you gave to the power in the XML, then click "Add". Then, in the text box on the right, enter the name of the power as you want it to appear in the karma power selection menu (e.g. "Lightning Strike").

Then, in the text box at the bottom, enter "UI/WonderMode/KarmaMenu:[PowerName]Description" (again minus the quotes and replacing "[PowerName]") and click "Add" again. On the right, enter the description for the power that will appear when it is selected in the karma menu.

Finally, click "Save" and commmit the changes. The preview of the STBL in S3PE should now look like this:



Repeat this process for each STBL (and language) you want to support. Note that any other text you display to the user should similarly be localized and added to these STBLs; check out the tutorial here for more info on how to do that. For this example, all additional strings will simply be hardcoded in English.

Creating the Power Booter
With all the resources in place, it's time to get coding! In this example, we'll take advantage of the existing "GetStruckByLightning" interaction to give Sims a healthy jolt of electricity when the power is activated.

Start by adding

Code:
using Gamefreak130.WonderPowersSpace.Booters;


At the top of then new file. Then, replace the default project class with the following Booter template:

Code:
namespace Gamefreak130.SampleLightningPowerSpace.Booters
{
    public class LightningPowerBooter : PowerBooter
    {
        
    }
}


Change the namespace and class name however you like, but leave the ": PowerBooter" untouched. This is what tells the mod that our class has information about karma powers that needs to be loaded.

You'll notice your IDE complaining about the class missing some required elements. This can be fixed by adding the following constructor inside the class:

Code:
public LightningPowerBooter() : base("Gamefreak130_SampleLightningPower")
{
}


Change "LightningPowerBooter" to match the containing class's name, and replace Gamefreak130_SampleLightningPower with the name you gave your XML resource. This constructor is what will allow the main mod to read the XML file and load the provided power info into the game!

It is worth noting that because this is executed when the game first loads into the main menu, if you have any extra state to set up or code to run OnPreLoad or OnWorldLoadFinished, you can create EventHandlers for these events elsewhere and add them as subscribers to the events inside the constructor. In this example, the tuning from the original GetStruckByLightning interaction will have to be copied to another custom interaction, so I'll add a call do a method that will do so:

Code:
public LightningPowerBooter() : base("Gamefreak130_SampleLightningPower")
{
    Common.Tunings.Inject(Sim.GetStruckByLightning.Singleton.GetType(), typeof(Sim), typeof(Interactions.LightningStrike.Definition), typeof(Sim), true);
}


The source code for this Inject method can be found in the GitHub repo linked above.

Writing the Activation Method
Now we'll write the method to run when the power is activated. Start by adding a static class with a static method in it, like so:

Code:
namespace Gamefreak130
{
    public static class SampleLightningPower
    {
        public static bool Activate(bool isBacklash)
        {
        }
    }
}


You will need a separate activation method for each power you plan to add, though all of these methods can exist under the same class and namespace. All power activation methods must take as a parameter a boolean indicating whether the power was activated as part of a karmic backlash instead of being purchased from the power selection menu; they must also return a boolean indicating whether the initial power activation completed successfully. Note that only bad powers can be selected for backlash, so if you set the "IsBad" value of the power to False in your XML, you can assume that "isBacklash" will always be false.

Since this example is a bad power that is valid for backlash and we need to find a Sim to be struck by lightning, I'll start the method with the following code:

Code:
Sim selectedSim = null;
if (isBacklash)
{
    List<Sim> validSims = Household.ActiveHousehold.Sims.FindAll(sim => sim.SimDescription.TeenOrAbove && sim.CanBeKilled() && !sim.IsInRidingPosture);
    if (validSims.Count > 0)
    {
        selectedSim = RandomUtil.GetRandomObjectFromList(validSims);
    }
}
else
{
    List<SimDescription> targets = PlumbBob.SelectedActor.LotCurrent.GetSims(sim => sim.SimDescription.TeenOrAbove && sim.CanBeKilled() && !sim.IsInRidingPosture)
                                                                    .ConvertAll(sim => sim.SimDescription);

    SimDescription selectedDescription = HelperMethods.SelectTarget(targets, "Lightning Strike");
    if (selectedDescription != null)
    {
        selectedSim = selectedDescription.CreatedSim;
    }
}

if (selectedSim == null)
{
    return false;
}


This might look intimidating, but it's really not too difficult to understand once you break it down. If the power was activated as part of the backlash, we don't give the user any choice in which Sim is selected to be struck; we find all Sims in the active household that can be struck (i.e. Teen or older, able to die from electrocution, and not riding a horse), and pick one at random if there are any. Otherwise, we get the list of Sims on the same lot as the currently selected Sim and convert it into a list of their associated SimDescriptions. HelperMethods.SelectTarget() is a method in Gamefreak130.WonderPowersSpace.Helpers that takes our list of SimDescriptions and opens a dialog for the user to select one; it then returns the selected SimDescription, or null if there were no Sims to pick from.

Note:
There is another method with the same name that allows for the selection of Lots as well; take a look at the "Meteor Strike" activation method for a usage example.

In fact, there are several helper methods in the Gamefreak130.WonderPowersSpace.Helpers namespace, such as WonderPowerManager.PlayPowerSting(), that are available for you to use. Again, feel free to look at the source for implementations and usage examples.
Finally, we get the original Sim back from the selected SimDescription if it exists, and if at the end we were not able to select a Sim, then we return false to signal that the power activation failed.

If we are able to select a valid Sim, then we'll need to focus on them and strike them with lightning. To do so, I'll add the following code:

Code:
Camera.FocusOnSim(selectedSim);
if (selectedSim.IsSelectable)
{
    PlumbBob.SelectActor(selectedSim);
}
InteractionInstance instance = new LightningStrike.Definition().CreateInstance(selectedSim, selectedSim, new InteractionPriority(InteractionPriorityLevel.CriticalNPCBehavior), false, false);
if (!instance.Test())
{
    return false;
}
selectedSim.InteractionQueue.AddNext(instance);
return true;


Fortunately, this part of the method is more straightforward. The interaction used here is a custom one that I will define shortly. Note that the interaction priority is set to CriticalNPCBehavior so that it will automatically stop all interactions except those relating to pregnancy, fire, or death, and that the last argument is set to "false", meaning that the interaction cannot be canceled by the user once added. If for whatever reason the test for adding the interaction instance fails (e.g. if there's an active fire on the lot), then we return false to notify the system of failure. Otherwise, we can add the interaction to the Sim and return true.

The final activation method, then, should look something like this:

Code:
using Gamefreak130.SampleLightningPowerSpace.Interactions;
using Gamefreak130.WonderPowersSpace.Helpers;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.CAS;
using Sims3.Gameplay.Core;
using Sims3.Gameplay.Interactions;
using System.Collections.Generic;

namespace Gamefreak130
{
    public static class SampleLightningPower
    {
        public static bool Activate(bool isBacklash)
        {
            Sim selectedSim = null;
            if (isBacklash)
            {
                List<Sim> validSims = Household.ActiveHousehold.Sims.FindAll(sim => sim.SimDescription.TeenOrAbove && sim.CanBeKilled() && !sim.IsInRidingPosture);
                if (validSims.Count > 0)
                {
                    selectedSim = RandomUtil.GetRandomObjectFromList(validSims);
                }
            }
            else
            {
                List<SimDescription> targets = PlumbBob.SelectedActor.LotCurrent.GetSims(sim => sim.SimDescription.TeenOrAbove && sim.CanBeKilled() && !sim.IsInRidingPosture)
                                                                                .ConvertAll(sim => sim.SimDescription);

                SimDescription selectedDescription = HelperMethods.SelectTarget(targets, "Lightning Strike");
                if (selectedDescription != null)
                {
                    selectedSim = selectedDescription.CreatedSim;
                }
            }

            if (selectedSim == null)
            {
                return false;
            }

            Camera.FocusOnSim(selectedSim);
            if (selectedSim.IsSelectable)
            {
                PlumbBob.SelectActor(selectedSim);
            }
            InteractionInstance instance = new LightningStrike.Definition().CreateInstance(selectedSim, selectedSim, new InteractionPriority(InteractionPriorityLevel.CriticalNPCBehavior), false, false);
            if (!instance.Test())
            {
                return false;
            }
            selectedSim.InteractionQueue.AddNext(instance);
            return true;
        }
    }
}


Now, the only code left to write is the LightningStrike interaction, which I've written as follows:

Code:

using Gamefreak130.WonderPowersSpace.Helpers;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Interactions;
using Sims3.SimIFace;

namespace Gamefreak130.SampleLightningPowerSpace.Interactions
{
public class LightningStrike : Sim.GetStruckByLightning
{
new public class Definition : InteractionDefinition<Sim, Sim, LightningStrike>
{
public override bool Test(Sim actor, Sim target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
{
return true;
}
}

public override bool Run()
{
try
{
return base.Run();
}
finally
{
WonderPowerManager.TogglePowerRunning();
}
}
}
}


If you've worked with custom interactions before, this code is fairly self-explanatory, since it just directly reuses the Run() method from GetStruckByLightning to generate the lightning strike. The only notable addition is WonderPowerManager.TogglePowerRunning(), another method from Gamefreak130.WonderPowersSpace.Helpers that signals to the system that all power effects have completely finished and that it is safe to re-enable the karma power selection menu so that another power can be chosen. (Karmic backlashes will also start at this time, if necessary.) All powers must call this method at the end of activation, or else the selection menu will be stuck in the disabled state!

Also note that the TogglePowerRunning() method is part of a try-finally block. Put simply, wrapping code in a finally block ensures that it will always be executed after the try block, even if an error occurs in the try block that would ordinarily halt code execution. This is an extremely important, but subtle point: there is no need for such try-finally blocks in the main activation method, since the system already has checks in place to undo power execution and re-enable the selection menu should an error occur, but if your power uses interactions, situations, or anything else that would exit the scope of the initial activation method, you MUST include error handling like this in them and test everything thoroughly to ensure that power activation can never be inadvertently left running in the event of script errors! You can take a look at the source code of the main mod for some more complex examples of error handling to ensure that power effects are applied and TogglePowerRunning() is called whenever possible.

Finishing the Package
If you've made it this far, congratulations! We're basically done, but there are a couple more finishing touches to make. In your IDE, compile your project into a DLL, then open up your saved package in S3PE. Right-click on the S3SA resource you created earlier, select "Import DLL", and find your compiled project DLL to add it to the package.

Finally, we'll need to link the powers to their activation methods when booting, so reopen the power booter XML in a text editor and enter the following between each power's "<EffectMethod>" tags:

Code:

[ClassName], [ProjectName], [MethodName]


Where "[ClassName]" is the fully qualified type name of the class containing the activation method, "[ProjectName]" is the name of the DLL (minus the .dll extension) containing the activation method, and "[MethodName]" is the name of the activation method itself. In the case of our Lightning Strike example, this is what the final XML would look like:

Code:
<?xml version="1.0" encoding="utf-8" ?>
<KarmaPowers>
<Power>
<ProductVersion>BaseGame</ProductVersion>
<PowerName></PowerName>
<IsBad></IsBad>
<Cost></Cost>
<EffectMethod></EffectMethod>
</Power>
<Power>
<ProductVersion>EP8</ProductVersion>
<PowerName>LightningStrike</PowerName>
<IsBad>True</IsBad>
<Cost>20</Cost>
<EffectMethod>Gamefreak130.SampleLightningPower, Gamefreak130.SampleLightningPower, Activate</EffectMethod>
</Power>
</KarmaPowers>


Once done, save your package to your mods folder and open the game. Your custom karma power should appear with all the others in the selection menu!



As always, feel free to ask any questions if any part of this guide was confusing or if there is something I can clarify
Screenshots

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Instructor
Original Poster
#3 Old 22nd Sep 2021 at 2:18 AM
Reserved, just in case

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Lab Assistant
THANKS POST
#4 Old 22nd Sep 2021 at 3:53 AM
This is very impressive, wanted Karma powers for ages, seemed liked the most interesting thing about console version.
Lab Assistant
THANKS POST
#5 Old 22nd Sep 2021 at 5:51 AM
This is amazing! Thank you so much.
Test Subject
#6 Old 22nd Sep 2021 at 6:00 AM
Genius ! I didn’t know I need this karma powers thing in my game until today, it seems so cool. Thanks !
I really like the Quake maker power !
I was wondering if it’s possible to add the utopian/dystopian effects from ITF (beautiful or desolate landscape) but in the current world as good/bad powers following this tutorial or is it too complex ?
Scholar
THANKS POST
#7 Old 22nd Sep 2021 at 6:08 AM
Wow! This is very clever! Thank you
Undead Molten Llama
THANKS POST
#8 Old 22nd Sep 2021 at 9:37 AM
This looks like so much fun, and I can't imagine the work involved to create it. I look forward to putting this in my game and testing it out. Thank you so much!
Lab Assistant
THANKS POST
#9 Old 22nd Sep 2021 at 9:58 AM
Oh man, this brings back so much of my childhood.
My first ever taste of Sims 3 was Sims 3 Pets on the Xbox 360 and I remember wreaking havoc with the karma powers
I cant wait to play with this in my game
Instructor
THANKS POST
#10 Old 22nd Sep 2021 at 11:23 AM
I'm sorry, but this is amazing and I love you now.

I never played any of the Sims games on console, so I didn't even realize there were such major differences. Thank you from the bottom of my heart for creating this mod!
Senior Moderator
staff: senior moderator THANKS POST
#11 Old 22nd Sep 2021 at 12:06 PM
This looks INCREDIBLE!!
Lab Assistant
THANKS POST
#12 Old 22nd Sep 2021 at 12:25 PM
Was just thinking about this the other day, thanks for the awesome mod!
Lab Assistant
THANKS POST
#13 Old 22nd Sep 2021 at 1:15 PM
OMGGGGGGGGG! I miss so MUCH Karma Power!!!! on my Xbox 360. Finally into the PC!!!! I gonna LOVE it!!! Thanks you so much modder for bring back my childhood since play on Xbox 360 ^_^
Field Researcher
THANKS POST
#14 Old 22nd Sep 2021 at 2:21 PM
Are you kidding meeeee this is massive!! Thank you so much!
Test Subject
#15 Old 22nd Sep 2021 at 3:15 PM
I am so impressed! I assume that the console games use the same engine then?
Forum Resident
THANKS POST
#16 Old 22nd Sep 2021 at 3:52 PM
Joining all my fellow simmers above : the brand new UI, the design and the features in this mod are extremely impressive. Amazing mod, gamefreak! Thank you for this upload.
Lab Assistant
#17 Old 22nd Sep 2021 at 4:26 PM
Quote: Originally posted by chazzhay
I am so impressed! I assume that the console games use the same engine then?


Yes, absolutely same as pc but Karma Power only available special for console. Glad gamefreak make bring back into PC. This make game more fun
Instructor
Original Poster
#18 Old 22nd Sep 2021 at 4:45 PM
Quote: Originally posted by chazzhay
I am so impressed! I assume that the console games use the same engine then?


Without getting too technical, the PC and Console engines and scripting cores aren't identical, but they are indeed very similar.

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Top Secret Researcher
THANKS POST
#19 Old 22nd Sep 2021 at 6:18 PM
Wow! Yes, I can well believe this took a long time to make, because it looks incredible! I haven't played TS3 on console, so I didn't realize that karma powers even existed. How amazing of you to add them to the PC game!

I doubt that I'll actually use this, because I'm playing a stripped-down version of TS3 in order to make it load faster, but I still wanted to exclaim and admire what you've made here.
Lab Assistant
#20 Old 22nd Sep 2021 at 9:27 PM Last edited by SecretX : 24th Sep 2021 at 9:43 PM.
Default French Translation!
Here is a French translation for your AWwesome mod, MERCI !
Attached files:
File Type: zip  Gamefreak130_KarmaPowers_FR.zip (47.32 MB, 33 downloads)
Field Researcher
THANKS POST
#21 Old 22nd Sep 2021 at 10:05 PM
Dang. I have never known these existed - time to have fun
Undead Molten Llama
#22 Old 22nd Sep 2021 at 11:39 PM Last edited by iCad : 23rd Sep 2021 at 1:08 PM.
I played with this in my game a little bit today, and I found what might be a problem. With this mod installed, my Sims don't gain job performance in their careers. Or rather, they do gain it but as soon as they leave the rabbithole, the meter goes back to zero. Now, I say this might be a problem because I have many, many other mods that this one might simply be interacting badly with. I did run the Dashboard and it's not showing conflict with anything, but I always take that with a grain of salt. All I know at this point is that with the mod in my game along with all my other stuff, job performance increases don't "stick," and without the mod in, job performance is fine.

I don't have time at the moment to set up a testing situation to look into this further, but I will do so probably tomorrow and report back in.


Never mind! It wasn't this mod that was causing the issue. It was another that I had forgotten I put in in order to test. Derp!

I'm mostly found on (and mostly upload to) Tumblr these days because, alas, there are only 24 hours in a day.
Muh Simblr! | An index of my downloads on Tumblr.
Forum Resident
THANKS POST
#23 Old 23rd Sep 2021 at 12:34 AM
What a great mod! Thank you so much for sharing this! I can't wait to use it in game.

Do you know if the karma power points stay the same amount if I switch out of the household or do they reset? I like to play rotationally so am curious about that aspect.
Test Subject
THANKS POST
#24 Old 23rd Sep 2021 at 12:38 AM
Oh my good. I'm dying. I wished for this for so long and then you just made it. This is so awesome. Thank you for your work.
Lab Assistant
THANKS POST
#25 Old 23rd Sep 2021 at 12:43 AM
Yo! I've been wanting this for years! Great job!
Page 1 of 4