Hi there! You are currently browsing as a guest. Why not create an account? Then you get less ads, can thank creators, post feedback, keep a list of your favourites, and more!
Quick Reply
Search this Thread
Inventor
Original Poster
#1 Old 31st Oct 2020 at 4:08 PM
Default Script modding: Modifying a specific social interaction
Hey there!

After, what, 12 years playing this game, I decided to dip into script modding. I started with something that I thought would be simple, but I can't find a tutorial about this specific thing!

I made some kelp recipes (https://modthesims.info/showthread.php?t=648901). I would like to modify the "Discuss Kelp Recipes" social interaction with mermaids to include a chance to learn one of these recipes. I think to do that, I need an event listener for this interaction.

The closest EventType I found was kSocialInteraction, which covers a lot of ground. I assume there's a way to drill down to which SPECIFIC social interaction has occurred.

This is the framework I've built so far. Can someone point me in the right direction?

Code:
namespace Echoweaver.Sims3Game.DiscussKelpRecipes
{
    public class DiscussKelp
    {
        [Tunable]
        protected static bool kInstantiator = false;

        private static readonly Random rollDice = new Random();
        private static readonly object syncLock = new object();

        static DiscussKelp()
        {
            World.sOnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinishedHandler);
        }

        public static void OnWorldLoadFinishedHandler(object sender, System.EventArgs e)
        {
            EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(OnSocialInteraction));            
        }

        public static ListenerAction OnSocialInteraction(Event e)
        {
            if (-- How do I determine which social interaction?? --) {
                LearnKelpRecipe(...);
            }
            return ListenerAction.Keep; 
        }

        public static bool LearnKelpRecipe(...)
        {
            if(actor.SkillManager.HasElement(SkillNames.Cooking))
            {
                int dieRoll = 0;
                lock(syncLock)
                {
                    // 1 in 6 chance you might learn a recipe
                    dieRoll = rollDice.Next(1, 6);
                }

                if (dieRoll == 6)  // 6 is the winning number
                {
                   // Check Cooking Skill level and which kelp recipes actor already knows
                }
        }

    }
}


Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Advertisement
Space Pony
#2 Old 31st Oct 2020 at 5:01 PM
Hi @echoweaver,

casting the generic event to the social event will give you the option of "asking" for the name of the interaction see code below

Code:
		static ListenerAction OnSocialInteraction(Event e)
		{
			SocialEvent cevent = ((SocialEvent)e);
			if ( cevent != null && cevent.SocialName == "Something with kelp")
			{
				DoStuff
			}
                        return ListenerAction.Keep; 
		}


2. Are you sure you need a sync lock for your code i dont see it beeing threaded ?
Inventor
Original Poster
#3 Old 31st Oct 2020 at 5:44 PM
Quote: Originally posted by Battery
2. Are you sure you need a sync lock for your code i dont see it beeing threaded ?


Thank you!!

The sync lock is for the random number generator. You can't access an object of class Random in a static method. StackOverflow suggested the sync lock. I know nothing about random number generation in C# and welcome suggestions.

Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Space Pony
#4 Old 31st Oct 2020 at 6:14 PM Last edited by Battery : 31st Oct 2020 at 6:36 PM.
Quote: Originally posted by echoweaver
Thank you!!

The sync lock is for the random number generator. You can't access an object of class Random in a static method. StackOverflow suggested the sync lock. I know nothing about random number generation in C# and welcome suggestions.


Sims 3 has its own "global" Randomgen you can just use
Code:
if(RandomUtil.GetInt(1,6) == 6){DoStuff;}
you can try it out and see if its "random" enough and you dont need this lock

Quote:
You can't access an object of class Random in a static method.

Code:
		static Random rnd = new Random();
		static void OnPreload()
		{
			double rand = rnd.NextDouble();
		}

Works fine for me

E: as for the stackoverflow thing dont take everything there as ground truth sometimes there are errors in the suggestions (just like here on the forums[yes this includes my suggestions aswell])



I think the discussion on stackoverflow might be about the be behaviour below
Code:
			Random rnd;
			for (int i = 0; i < 6; i++) 
			{
				rnd = new Random();
				Console.WriteLine(rnd.NextDouble());
			}

Possible output:
0,97177004300606
0,97177004300606
0,97177004300606
0,97177004300606
0,97177004300606

while
Code:
			Random rnd = new Random();
			for (int i = 0; i < 6; i++) 
			{
				Console.WriteLine(rnd.NextDouble());
			}

Possible output:
0,160861104801698
0,97177004300606
0,731557397512513
0,864868871804731
0,783934373307943
Inventor
Original Poster
#5 Old 31st Oct 2020 at 8:24 PM
Quote: Originally posted by Battery
Code:
		static Random rnd = new Random();
		static void OnPreload()
		{
			double rand = rnd.NextDouble();
		}

Works fine for me

E: as for the stackoverflow thing dont take everything there as ground truth sometimes there are errors in the suggestions (just like here on the forums[yes this includes my suggestions aswell])




Yeah, I was declaring the random object once as an attribute of the class and reusing it. That doesn't work (or the IDE claims it doesn't, since I haven't compiled yet) in the static method. Calling the same object multiple times is supposed to use fewer resources and be more random.

BUT, now that I know about the Sims util, that is much better, so it doesn't matter.

I really appreciate the help.

ETA: Dur, I guess that's what you did in your first example. And also, now the IDE is no longer indicating problems for me either. I'm confused, but it doesn't matter because I'm going to switch to the other random utility.

Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Inventor
Original Poster
#6 Old 1st Nov 2020 at 1:12 AM
So, my next question is how one identifies recipes. The Recipe class has a GenericName, which is a string, and a static dictionary connecting a string name to a Recipe object.

I'm assuming the string name is the classname of the recipe that I defined and hashed when I created the recipes using CCLoader. Thus, my first draft of the mod looks like this. It compiled, and I'm still going through the tutorial bits to get it ready to test.

Code:
        public static ListenerAction OnSocialInteraction(Event e)
        {
            SocialEvent cevent = ((SocialEvent)e);
            if (cevent != null && cevent.SocialName == "Discuss Kelp Recipes")
            {
                LearnKelpRecipe(cevent.Actor as Sim);
            }
            return ListenerAction.Keep;
        }

        public static bool LearnKelpRecipe(Sim actor)
        {
            if (actor.SkillManager.HasElement(SkillNames.Cooking))
            {
                if (RandomUtil.GetInt(1, 6) == 6)  // 1 in 6 chance of learning a recipe
                {
                    // Learn the lowest level recipe the sim doesn't already know, providing Cooking skill is high enough

                    Cooking simCooking = actor.SkillManager.GetElement(SkillNames.Cooking) as Cooking;
                    Recipe kelpRecipe = new Recipe();

                    if ((kelpRecipe = Recipe.NameToRecipeHash["Echoweaver_SeaweedSalad"]) != null
                        && simCooking.SkillLevel >= kelpRecipe.CookingSkillLevelRequired
                        && !simCooking.KnownRecipes.Contains(kelpRecipe.GenericName))
                    {
                        simCooking.AddRecipe(kelpRecipe);
                        return true;
                    }
                    else if ((kelpRecipe = Recipe.NameToRecipeHash["Echoweaver_MisoSoup"]) != null
                        && simCooking.SkillLevel >= kelpRecipe.CookingSkillLevelRequired
                        && !simCooking.KnownRecipes.Contains(kelpRecipe.GenericName))
                    {
                        simCooking.AddRecipe(kelpRecipe);
                        return true;
                    }
                    else if ((kelpRecipe = Recipe.NameToRecipeHash["Echoweaver_FishSandwich"]) != null
                        && simCooking.SkillLevel >= kelpRecipe.CookingSkillLevelRequired
                        && !simCooking.KnownRecipes.Contains(kelpRecipe.GenericName))
                    {
                        simCooking.AddRecipe(kelpRecipe);
                        return true;
                    }
                    else if ((kelpRecipe = Recipe.NameToRecipeHash["Echoweaver_SpicyTuna"]) != null
                        && simCooking.SkillLevel >= kelpRecipe.CookingSkillLevelRequired
                        && !simCooking.KnownRecipes.Contains(kelpRecipe.GenericName))
                    {
                        simCooking.AddRecipe(kelpRecipe);
                        return true;
                    }
                    // Guess you already know all the kelp recipes
                }
                // Aww, you didn't roll a winning number
            }
            // No recipe learned
            return true;
        }


Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Space Pony
#7 Old 1st Nov 2020 at 3:26 PM Last edited by Battery : 1st Nov 2020 at 3:39 PM.
Well if you dont know your recipe name you always could loop through all recipes and spit out a message containing all of them then just look for yours and voila the name got served.
There is probably a more targeted approach(i never worked with recipes myself) but this one is easy.

E: for a more targeted approch see how you are getting your recipes into the game and see if theres the name somwhere
Inventor
Original Poster
#8 Old 1st Nov 2020 at 5:00 PM Last edited by echoweaver : 1st Nov 2020 at 5:52 PM.
Well, I know *A* name that I gave my recipes when I created them. I just don't know how those methods work or if there are other identifiers I might be using. Is good with the text name? Does it expect the hashed hex code? I've seen stuff indicating that the game considers the "true" food to be the foodEat part, so it could be that. Is there some other identification it cares about?

I'm using the the pre-hashed string recipe name itself, without "foodEat," which is what I named the cooking process. I'll just test and see what it does .

Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Field Researcher
#9 Old 3rd Nov 2020 at 2:13 AM
Hey, this looks like a really cool mod! Also I just wanted to mention something I've noticed using this event - it seems like a separate kSocialInteraction event is fired for each sim in an interaction (not sure if this always happens or just sometimes, I haven't been paying that close attention to my debugging messages about it), which I believe in your case could give the actor 2 chances per interaction to learn a recipe instead of just 1. I've been using a solution to that problem that I saw in one of the NRaas mods, checking SocialEvent.WasRecipient before proceeding to avoid doubling up.
Back to top