PDA

View Full Version : Custom Buffs


ani_
28th Jan 2012, 05:11 PM
I want to add a custom Moodlet to my mod.

I've been looking at Twallan's WooHooer, and Velocityrass' instructions (http://modthesims.info/showthread.php?t=430540&highlight=moodlet). I know what I'm suppose to do, but not sure how to do it.

My question is, how do I add a new buff to the sDictionary,

GenericManager<BuffNames, BuffInstance, BuffInstance>.sDictionary.Add((ulong)guid, value);

When the sDictionary is protected, and can't be accessed.

twallan
28th Jan 2012, 08:27 PM
The Sims 3 Scripting Core is operated by a MONO interpreter, which does not do any access-level permission checking at run-time.

Because of this quirk you can actually compile your mods against custom Core dlls that have different access-levels than the files used by the game itself.

So, what most of us do is the following :
1) Export the Core S3SA dlls to file
2) Decompile them using ILDASM
3) Change the permissions to "public" (I simply run a script that changes them all, but you only need to change the ones you require)
4) Recompile the files using ILASM
5) Use the altered version of the Core dlls to compile your scripting

You don't have to include the altered Core files in your mod package, since all you are changing is access-level permissions, and the MONO interpreter does not check those anyways.

You can read more about it in this thread, where everyone tries to explain it to jtravers88 : http://www.modthesims.info/showthread.php?p=3360427

:)

ani_
28th Jan 2012, 09:07 PM
Thank you so much \o/

lenglel
29th Jan 2012, 06:17 AM
Twallan, is that script to change access permissions available for download?

I'd like to change permissions in the assemblies I'm compiling against, without
having to change them manually.

twallan
29th Jan 2012, 05:52 PM
using System.IO;
using System.Text.RegularExpressions;



List<Tuple<string, string>> regEx = new List<Tuple<string, string>>();
regEx.Add(new Tuple<string, string>("(\\s)assembly(\\s)", "$1public$2"));
regEx.Add(new Tuple<string, string>("(\\s)family(\\s)", "$1public$2"));
regEx.Add(new Tuple<string, string>("(\\s)private(\\s)", "$1public$2"));
regEx.Add(new Tuple<string, string>("(\\s)sealed(\\s)", "$1"));
regEx.Add(new Tuple<string, string>("(\\s)initonly(\\s)", "$1"));
regEx.Add(new Tuple<string, string>("\\.field (|static )assembly", ".field $1 public"));
regEx.Add(new Tuple<string, string>("\\.method public hidebysig specialname instance void(\\s*\\n\\s*add_)", ".method private hidebysig specialname instance void$1"));
regEx.Add(new Tuple<string, string>("\\.method public hidebysig specialname instance void(\\s*\\n\\s*remove_)", ".method private hidebysig specialname instance void$1"));
regEx.Add(new Tuple<string, string>("\\.method public hidebysig newslot specialname virtual(| final)(\\s*\\n\\s*instance void add_)", ".method private hidebysig newslot specialname virtual $1$2"));
regEx.Add(new Tuple<string, string>("\\.method public hidebysig newslot specialname virtual(| final)(\\s*\\n\\s*instance void remove_)", ".method private hidebysig newslot specialname virtual $1$2"));
regEx.Add(new Tuple<string, string>("\\.method public hidebysig specialname static(\\s*\\n\\s*void add_)", ".method private hidebysig specialname static$1"));
regEx.Add(new Tuple<string, string>("\\.method public hidebysig specialname static(\\s*\\n\\s*void remove_)", ".method private hidebysig specialname static$1"));

string contents = null;

try
{
Application.UseWaitCursor = true;

using (StreamReader file = new StreamReader(filename))
{
contents = file.ReadToEnd();
}

foreach (Tuple<string, string> value in regEx)
{
Regex regex = new Regex(value.Item1);

contents = regex.Replace(contents, value.Item2);
}

using (StreamWriter file = new StreamWriter(filename))
{
file.Write(contents.ToCharArray());
}
}
finally
{
Application.UseWaitCursor = false;
}

lenglel
30th Jan 2012, 01:01 AM
Thank you so much!

Buzzler
31st Jan 2012, 06:37 PM
@ani_: You may want to take a look at BuffManager.CreateBuffTable() and how it loads the buffs for the store objects. Imitating that is the way to go IMHO.

GnatGoSplat
1st Mar 2012, 05:13 PM
I'd like to learn how to add custom moodlets too. To imitate the way store objects load buffs, do I need to duplicate CreateBuffTable() into my own project and change the hash instance name from buffs_store to a custom one?

Twallan, does your script need to be compiled for .NET Framework 4? When I try it in VS.NET 2008, it doesn't recognize Tuple and Google search pulls up an MSDN page that says it's for .NET Framework 4.

Buzzler
1st Mar 2012, 06:25 PM
I'd like to learn how to add custom moodlets too. To imitate the way store objects load buffs, do I need to duplicate CreateBuffTable() into my own project and change the hash instance name from buffs_store to a custom one?Right on. I just whipped that together as a reference. Call the Init method from the static constructor of your mod. My way to initialize and call that stuff is different, but the content from LoadBuffData and AddBuffs is right from one of my mods.

public static void Init()
{
LoadSaveManager.ObjectGroupsPreLoad += new ObjectGroupsPreLoadHandler(LoadBuffData);
}
private static void LoadBuffData()
{
this.AddBuffs(null);
Sims3.UI.UIManager.NewHotInstallStoreBuffData += new Sims3.UI.UIManager.NewHotInstallStoreBuffCallback(this.AddBuffs);
}
private static void AddBuffs(ResourceKey[] resourceKeys)
{
ResourceKey key = new ResourceKey(ResourceUtils.HashString64("YOUR_XML_NAME_HERE"), 0x0333406C, 0x0);
XmlDbData data = XmlDbData.ReadData(key, false);
if (data != null)
{
BuffManager.ParseBuffData(data, true);
}
}

twallan
1st Mar 2012, 07:51 PM
Twallan, does your script need to be compiled for .NET Framework 4? When I try it in VS.NET 2008, it doesn't recognize Tuple and Google search pulls up an MSDN page that says it's for .NET Framework 4.

It is an application external to Sims 3, so it is not restricted to a specific version in the same way mods are. So, yes I build it using the most up-to-date version of .NET.

Tuple is a simple class to construct yourself though, if you so choose. :)

misukisu
2nd Mar 2012, 06:10 AM
Twallan, huge thanks for sharing your script and explaining how to access private fields! I've been struggling with this same problem too.


EDIT: And it works like a charm! I've so been in need of this.

GnatGoSplat
2nd Mar 2012, 02:23 PM
Buzzler, thanks, it's good to know my thought process is on the right track.

Twallan, I got the script to work, had to make a Form for it and add an open file dialog. Thanks for sharing it! I don't know C# or OOP at all so the learning curve is pretty steep for me, so I'm pretty happy about even figuring out how to make that script work!

EDIT: Can't hardly believe it, but I got it to work! Got a clean compile thanks to Twallan's script. I don't have any code to trigger it yet, but I can add my moodlet to the sim using Master Controller. I noticed the store items use filetype of BUFF instead of _XML. I can't tell by the code that it matters what filetype I use as long as it matches what the function is looking for. Since I had originally copy and pasted that function and just changed it in my DLL, I left the type as BUFF. Is there any compelling reason to use one vs the other?

pljones
17th Mar 2012, 02:44 PM
Here's a little program to make this even easier. MOVED!! - see below

Drop a .Net DLL onto the .exe and it'll disassemble it, unprotect it and reassemble it "in place" (taking a backup).

It has prerequisites of ILDASM and ILASM.

Source included in the .7z for those interested.

Thanks to Twallan for the method of unprotecting.

---
This tool now has a proper home, rather than hiding here in this thread. It has joined the various other sims3tools hosted over at Simlogical: here (http://www.den.simlogical.com/denforum/index.php?topic=1689.0)!

Consort
3rd Apr 2012, 09:15 PM
Thank you very much for this thread everyone, it has been extremely helpful.

Ok I have a problem I can't seem to resolve.

I got Velocitygrasses naked toilet mod to work, so my general setup seems to be valid.
.
However, when I unprotect the SimIFace.dll using pljones .exe I get 1 error saying
'Sims3.SimIFace.World' does not contain a definition for 'OnWorldLoadFinishedEventHandler' D:\Documents\Visual Studio 2010\Projects\usetoiletnaked\VelocitygrassSims3Test\test_code.cs 31 19 VelocitygrassSims3Test

What's going on there? I'd like to access private methods in SimIFace but that's no fun when the eventhandler does'nt work:) Why would the compiler not find it when the dll is unprotected?

twallan
5th Apr 2012, 07:08 PM
You should be careful using my approach on SimIFace.

The regular expressions I use presume there are equivalent member fields for each delegate event, and proceeds to hide all the add_ and remove_ functions to prevent name conflicts.

In the case of OnWorldLoadFinishedEventHandler(), it is hidden in favor of using sOnWorldLoadFinishedEventHandler directly, which may conflict with existing coding.

:)

Consort
6th Apr 2012, 11:57 PM
@twallan
Uhm thanks for your explanation but I'm afraid I'm too dumb to understand what your'e saying :)

You're saying you are intentionally hiding the OnWorldLoadFinishedEventHandler with your script and it's fine to use sOnWorldLoadFinishedEventHandler instead?

nonamena
3rd May 2012, 03:25 PM
Thanks to this excellent thread I've made progress towards adding custom moodlets to my custom food mod, but I'm still not quite there. I'm able to get the script to compile, but my own custom moodlets aren't showing up. They are not even being loaded into the game (couldn't see the with MasterController). I've tried both velocitygrass' method and Buzzler's method, and I've looked at Woohooer as well. I think I'd like to use Buzzler's method/store method, because it seems the easiest, if I can get it to work. My quetsions follow, but I'm such a beginner at this they might not even make sense. If that's the class, I apologize.

1. Regarding Buzzler's/Store method, if I'm using this with a object scriptmod, do I still need to call the init method from the static constructor? Can I just create a new class to imitate CreateBuffTable() ?
2. EA's Store buffs all have guids, and they are being used instead of hashstrings. Does it matter if I don't create a guid for my buffs? And how can I create a guid for the buffs?

Also, thanks very much, peter, for the unprotect program, it works like charm. (edit for nvm)

twallan
3rd May 2012, 08:10 PM
You're saying you are intentionally hiding the OnWorldLoadFinishedEventHandler with your script and it's fine to use sOnWorldLoadFinishedEventHandler instead?

Sorry, did not notice your post until now.

Yes, using sOnWorldLoadFinishedEventHandler directly would be an appropriate solution, or simply change the permissions on the delegate function back to public.

:)

Buzzler
5th May 2012, 02:39 PM
nonamena, it's probably easiest if you just take a look at my Roundhouse mod. It's small and simple and comes with a custom buff, so it should be a perfect reference. :)

The code to load the buff is just this:private void LoadBuffData()
{
this.AddBuffs(null);
Sims3.UI.UIManager.NewHotInstallStoreBuffData += new Sims3.UI.UIManager.NewHotInstallStoreBuffCallback(this.AddBuffs);
}
private void AddBuffs(ResourceKey[] resourceKeys)
{
ResourceKey key = new ResourceKey(ResourceUtils.HashString64("TwoBTech_ChuckNorris_Buffs"), 0x0333406C, 0x0);
XmlDbData data = XmlDbData.ReadData(key, false);
if (data != null)
{
BuffManager.ParseBuffData(data, true);
}
}
If you look at the BuffChuckNorris class, you'll see that it's basically empty. :) The StaticGuid property is how I address the buff inside the code. The related ulong value is the one that is in the TwoBTech_ChuckNorris_Buffs XML resource in the Hex node.

HTH

nonamena
7th May 2012, 12:23 PM
Buzzler, thanks very much for answering. This has helped a lot! I really appreciate it. I'm still unsure about how you converted BuffChuckNorris=0xEA82B27204EF370B into a ulong? Did you use GetGuid from the BuffManager? I have no idea how to do this and I think, maybe, this might be why my buffs are not actually being loaded into the game.

Buzzler
7th May 2012, 01:29 PM
I'm not quite sure what you mean. ;) The value isn't calculated at runtime or anything. It's in the XML like that and in the code like this
private const ulong kChuckNorrisGuid = 0xEA82B27204EF370BL;

The actual value doesn't matter at all. It just has to be the same in both cases. I've used the FNV64 hash of "BuffChuckNorris", though.

nonamena
7th May 2012, 02:36 PM
Haha. I see. When I de-compile your script with ILSpy it looks like this:
private const ulong kChuckNorrisGuid = 16898264954626324235uL;

But I knew that 0xEA82B27204EF370BL was the FNV64 hash of BuffChuckNorris, so as you might imagine, I was completely and utterly stupidly confused. But now I know! And I feel a little silly.

But my moodlets still won't load :( So if you have a spare moment at some point and you're feeling bored, perhaps you could take a look (if you don't have time or the inclination to look, I understand). The script for the object is working: the sim can eat an object that uses my script class, and the sims's hunger motive will increase. But no custom moodlets are being applies (or loaded). I think I know why but I don't know how to fix it.

Right now, only BuffTastyTreat is "ready" for testing, which I'm trying to apply when a sim eats a NonaDriedFoodLow object. I don't have the images or anything for the other two, and the STBLs aren't done.

Buzzler
7th May 2012, 05:49 PM
Shoot, I thought that would be obvious... noticed the IWantToKnowStuff thing in my BuffBooter declaration and the Subscribe() method? That's for my class loader (not sure if I should dare to call it framework *g*). After startup it searches for all classes that implement that interface, instantiates them and calls the Subscribe() method and passes itself as parameter. That way I don't have to do everything manually and have looser coupling.


Anyway, try to put this into the static constructor of your Instantiator class:
LoadSaveManager.ObjectGroupsPreLoad += OnPreLoad;
(Hint: That will only work if you altered the libraries to make that event accessible. IIRC anyway.)

plus this callback method:
private static void OnPreLoad()
{
(new BuffBooter()).LoadBuffData();
}Disclaimer: The above code is written manually into the reply and untested. ;)

Long story short: your BuffBooter class never gets instantiated and the LoadBuffData() method never called right now. ;)

nonamena
8th May 2012, 01:49 PM
Long story short: your BuffBooter class never gets instantiated and the LoadBuffData() method never called right now. ;)
Thanks again, Buzzler. I had a feeling that was the problem, but I wasn't sure how to fix it.

I tried for the rest of the day yesterday to make the script work, using your advice and whatever variations I could think of. I can compile the script (so my libraries are ok. I used peter's unprotect program and have completely removed all references and then re-added them several times to be sure) I still can't get my custom buffs loaded into the game. I'd really love to get this working, but I seem to be in over my head (typical for me).

If you have another moment when you're bored and have nothing better to do, I'd greatly value your insight. Thanks again for all your help with this already.

Edit: We solved it. The problem was my Buffs XML.

treeag
8th Sep 2012, 11:20 AM
Here's a little program to make this even easier.

Drop a .Net DLL onto the .exe and it'll disassemble it, unprotect it and reassemble it "in place" (taking a backup).

It has prerequisites of ILDASM and ILASM.

Source included in the .7z for those interested.

Thanks to Twallan for the method of unprotecting.

For some reason, after I did this and reload the dll in Visual C# 2008 Express it doesn't recognize the dll anymore. Any idea what I'm doing wrong?

nonamena
30th Dec 2012, 01:32 PM
Since this thread is still really popular for people looking to make custom moodlets, I thought I'd post a link to a tutorial I made (based on this thread and another) for custom moodlets: http://www.den.simlogical.com/denforum/index.php?topic=1063.0

pljones
9th Jan 2013, 09:07 PM
I thought I'd pushed this to SourceForge an age ago... but here it is:
http://sourceforge.net/p/sims3tools/code/1119/tree/trunk/Unprotect/

This has been updated and tested - thanks to Nona - on Windows8.

ETA: The tool now has its own home: Unprotect DLLs with sims3tools "Unprotect"! (http://www.den.simlogical.com/denforum/index.php?topic=1689.0) over at Simlogical.