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!
Field Researcher
Original Poster
#1 Old 22nd Aug 2022 at 4:16 PM
Default Remove Shoes Mod Question.
Earlier this month when I was working on my previous project. It occured to me that there was an object in the game that may remove the sims shoes when in use. That item was the firewalk object, and I was filled with happiness when I saw the object interaction remove the sims shoes when it occured.

With that in mind I have wanted to create a mod that is similar to the object from Sims 4 Snowy Escape where you can have sims remove their shoes while indoors. So, I took a peak in ILSpy to take a look at the firewalk object code. And, I happen to find codes that may help me accomplish the removal of Sims shoes.

At the moment, I would like to figure out how to establish the method to remove shoes. So I would like to practice an immediate interaction where the sim removes their shoes. So here are the codes that I found that may be useful. I will admit I have had a hard time understanding this code. So, I will try to explain what I think the codes are used for. I would really appreciate it if anyone more competent in script modding could help me figure out which lines of code can help me achieve my goal.

Code:
public void RemoveOutfitCallback(StateMachineClient sender, IEvent evt)
        {
            RemoveBarefootOutfit();
        }

I believe the code above is used to bring back the sims shoes whenever the firewalk interaction is finished.

Code:
public void AnimationCallback(StateMachineClient sender, IEvent evt)
        {
            switch (evt.EventId)
            {
                case 100u:
                    if (mSwitchOutfitHelper != null)
                    {
                        Actor.RefreshCurrentOutfit(resetIndex: false);
                        mSwitchOutfitHelper.ChangeOutfit();
                    }
                    break;
            }
        }

Honestly at a loss with this one.

Code:
private void SetupOutfitSwapIfNeeded()
        {
            SetParameter("playHop", paramValue: false);
            if (Actor.Service is GrimReaper || !IsWearingShoes())
            {
                return;
            }
            SimDescription simDescription = Actor.SimDescription;
            simDescription.RemoveSpecialOutfit(kSpecialOutfitName);
            SimBuilder simBuilder = new SimBuilder();
            simBuilder.UseCompression = true;
            OutfitUtils.SetOutfit(simBuilder, Actor.CurrentOutfit, simDescription);
            CASPart[] parts = Actor.CurrentOutfit.Parts;
            for (int i = 0; i < parts.Length; i++)
            {
                CASPart part = parts[i];
                if (part.BodyType == BodyTypes.Shoes)
                {
                    simBuilder.RemovePart(part);
                }
                string barefootCASPartName = GetBarefootCASPartName();
                ResourceKey resourceKey = new ResourceKey(ResourceUtils.HashString64(barefootCASPartName), 55242443u, ResourceUtils.ProductVersionToGroupId(ProductVersion.BaseGame));
                if (resourceKey != ResourceKey.kInvalidResourceKey)
                {
                    CASPart part2 = new CASPart(resourceKey);
                    simBuilder.AddPart(part2);
                    ResourceKey key = simBuilder.CacheOutfit("FireWalkPitBarefoot" + simDescription.SimDescriptionId);
                    SimOutfit outfit = new SimOutfit(key);
                    int num = simDescription.AddSpecialOutfit(outfit, kSpecialOutfitName);
                    if (num != -1)
                    {
                        mSwitchOutfitHelper = new Sim.SwitchOutfitHelper(Actor, OutfitCategories.Special, num);
                        mSwitchOutfitHelper.Start();
                        mSwitchOutfitHelper.Wait(idleIfNecessary: true);
                        AddOneShotScriptEventHandler(100u, AnimationCallback);
                        SetParameter("playHop", paramValue: true);

                    }
                }
            }
        }

I believe the method above is actually the method used to remove sim shoes. However, I'm not too sure whether each line of code is necessary.
Code:
private bool IsWearingShoes()
        {
            CASPart[] parts = Actor.CurrentOutfit.Parts;
            for (int i = 0; i < parts.Length; i++)
            {
                CASPart cASPart = parts[i];
                if (cASPart.BodyType == BodyTypes.Shoes)
                {
                    return !StringUtil.ContainsAny(CASUtils.PartDataGetName(cASPart.Key), "ShoesNude");
                }
            }
            return false;
        }

I believe this just makes the method in which it checks whether sims have shoes on or not. If the sim does then the code takes effect. Otherwise it returns false.
Code:
private string GetBarefootCASPartName()
        {
            string text = "";
            SimDescription simDescription = Actor.SimDescription;
            text = (simDescription.YoungAdult ? "a" : OutfitUtils.GetAgePrefix(simDescription.Age));
            text += OutfitUtils.GetGenderPrefix(simDescription.Gender);
            return text + "ShoesNude";
        }

Not sure what this does entirely.

Code:
private void RemoveBarefootOutfit()
        {
            {
                if (Actor.IsWearingSpecialOutfit(kSpecialOutfitName))
                {
                    Actor.SwitchToPreviousOutfitWithoutSpin();
                    Actor.SimDescription.RemoveSpecialOutfit(kSpecialOutfitName);
                }
            }
        }

This one just adds the shoes back I believe.

Again any help would be appreciated.
Advertisement
Space Pony
#2 Old 23rd Aug 2022 at 2:58 AM
It sounds like you're on the right track here. If you just want to make an immediate interaction that removes shoes (or, rather, adds the special barefoot firewalk outfit), you could take the SetupOutfitSwapIfNeeded() method as your Run method, remove the references to the "playHop" parameter and the AnimationCallback script event handler, and paste the innermost block of the AnimationCallback function below the mSwitchOutfitHelper initialization:



You'll also want to clean up the mSwitchOutfitHelper once the interaction is finished:



Then to undo the effects, you can just use the RemoveBarefootOutfit() method.

Note that the snippet above will not play any animations. If you want the little hopping animation from the firewalk pit to play, you might be able to hack together a custom Jazz script with just the hop states and readd the EnterStateMachine, AnimateSim, and ScriptEventHandler related stuff to the interaction. In that case, your package would become dependent on EP10, unless you copied the "a_firewalkPit_hop_change_x" CLIP resource into your package.

Quote: Originally posted by MonocoDoll
[AnimationCallback]
Honestly at a loss with this one.


Certain animations have event triggers that fire at certain points during playback. Just like you can use EventListeners to track and respond to scripting events, you can listen for and respond to these animation events using the AddOneShotScriptEventHandler() method, passing in the event ID and a function to run when the event fires.

In this case, presumably the hopping animation fires an event with an ID of 100 mid-hop, which triggers the AnimationCallback function that actually removes the Sim's shoes.

Quote: Originally posted by MonocoDoll
[GetBarefootCASPartName]
Not sure what this does entirely.


There are different variants of the "barefoot" shoes for each age and for each gender, and it's generally not a good idea to put clothing items on a Sim that were intended for a different age or gender. The GetBarefootCASPartName() just looks at the Actor's age and gender and puts together the correct identifier for the "barefoot" shoes that Sim should use.

"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
Field Researcher
Original Poster
#3 Old 23rd Aug 2022 at 11:50 PM
Thank you, so much for explaining the code. With that information, I went ahead and attempted to make the immediate interaction to remove shoes. However, the Remove Shoes Interaction does not work. I have made the interaction appear, but when I select the option it does not execute. Perhaps I did something wrong. My only hunch is that it may be the value 0x34AEECBU? I know from experience that the previous value 55242443u that was given from ILSpy is incorrect, because even from my other script mod, it would always change the values when looking at it from ILSpy. Even though I wrote a similar value that would look like 0x526AAEBU for example.

Code:
public class RemoveShoes : ImmediateInteraction<Sim, Sim>
    {

        public class Definition : InteractionDefinition<Sim, Sim, RemoveShoes>
        {
            public override bool Test(Sim actor, Sim target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
            {
                if (!actor.IsActiveSim || actor.IsWearingSpecialOutfit(kSpecialOutfitName)) 
                {
                    return false;
                }
                else
                {
                    return true;
                }

            }

            public override string GetInteractionName(Sim actor, Sim target, InteractionObjectPair iop)
            {
                return Localization.LocalizeString(actor.IsMale, "MonoDoll/RemoveShoes/RemoveShoes:InteractionName");
            }
        }

        [Tunable]

        public static InteractionDefinition Singleton = new Definition();

        public override bool Run()
        {
            SetupOutfitSwapIfNeeded();
            Cleanup();
            return true;
        }

        private void SetupOutfitSwapIfNeeded()
        {
            if (Actor.Service is GrimReaper || !IsWearingShoes())
            {
                return;
            }
            SimDescription simDescription = this.Actor.SimDescription;
            simDescription.RemoveSpecialOutfit(FirewalkPit.Firewalk.kSpecialOutfitName);
            SimBuilder simBuilder = new SimBuilder();
            simBuilder.UseCompression = true;
            OutfitUtils.SetOutfit(simBuilder, this.Actor.CurrentOutfit, simDescription);
            CASPart[] parts = Actor.CurrentOutfit.Parts;
            foreach (CASPart part in this.Actor.CurrentOutfit.Parts)
            {
                if (part.BodyType == BodyTypes.Shoes)
                {
                    simBuilder.RemovePart(part);
                }
            }
            string barefootCASPartName = this.GetBarefootCASPartName();
            ResourceKey resourceKey = new ResourceKey(ResourceUtils.HashString64(barefootCASPartName), 0x34AEECBU, ResourceUtils.ProductVersionToGroupId(ProductVersion.BaseGame));
            if (resourceKey != ResourceKey.kInvalidResourceKey)
            {
                CASPart part2 = new CASPart(resourceKey);
                simBuilder.AddPart(part2);
                ResourceKey key = simBuilder.CacheOutfit("FirewalkPitBarefoot" + simDescription.SimDescriptionId);
                SimOutfit outfit = new SimOutfit(key);
                int num = simDescription.AddSpecialOutfit(outfit, FirewalkPit.Firewalk.kSpecialOutfitName);
                if (num != -1)
                {
                    this.mSwitchOutfitHelper = new Sim.SwitchOutfitHelper(this.Actor, OutfitCategories.Special, num);
                    this.mSwitchOutfitHelper.Start();
                    this.mSwitchOutfitHelper.Wait(true);
                    this.Actor.RefreshCurrentOutfit(false);
                    this.mSwitchOutfitHelper.ChangeOutfit();
                }
                return;
            }
        }

        public override void Cleanup()
        {
            if (this.mSwitchOutfitHelper != null)
            {
                this.mSwitchOutfitHelper.Dispose();
                this.mSwitchOutfitHelper = null;
            }
            base.Cleanup();
        }
        private bool IsWearingShoes()
        {
            CASPart[] parts = Actor.CurrentOutfit.Parts;
            for (int i = 0; i < parts.Length; i++)
            {
                CASPart cASPart = parts[i];
                if (cASPart.BodyType == BodyTypes.Shoes)
                {
                    return !StringUtil.ContainsAny(CASUtils.PartDataGetName(cASPart.Key), "ShoesNude");
                }
            }
            return false;
        }

        private string GetBarefootCASPartName()
        {
            string text = "";
            SimDescription simDescription = Actor.SimDescription;
            text = (simDescription.YoungAdult ? "a" : OutfitUtils.GetAgePrefix(simDescription.Age));
            text += OutfitUtils.GetGenderPrefix(simDescription.Gender);
            return text + "ShoesNude";
        }

        private static string kSpecialOutfitName = "FirewalkBarefoot";

        private Sim.SwitchOutfitHelper mSwitchOutfitHelper;
    }
Space Pony
#4 Old 24th Aug 2022 at 12:24 AM
0x34AEECB is the correct type code. It's actually the same number as 55242443; the former is written in hexidecimal, which basically means that digits go from 0-9 and then A-F before you reach 10 (and thus 0x10 actually equals 16).

I'm not sure what the issue could be here, exactly. Here are a couple things I'd try, in no particular order:
  • Add a call to StyledNotification.Show() just before the ChangeOutfit() method call, as a sanity check to ensure that the interaction is actually reaching that point in the code
  • Change the interaction class to Interaction<> instead of ImmediateInteraction<>
  • Change mSwitchOutfitHelper.Wait(true) to mSwitchOutfitHelper.Wait(false)

"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
Field Researcher
Original Poster
#5 Old 24th Aug 2022 at 12:53 AM
Quote: Originally posted by gamefreak130
0x34AEECB is the correct type code. It's actually the same number as 55242443; the former is written in hexidecimal, which basically means that digits go from 0-9 and then A-F before you reach 10 (and thus 0x10 actually equals 16).

I'm not sure what the issue could be here, exactly. Here are a couple things I'd try, in no particular order:
  • Add a call to StyledNotification.Show() just before the ChangeOutfit() method call, as a sanity check to ensure that the interaction is actually reaching that point in the code
  • Change the interaction class to Interaction<> instead of ImmediateInteraction<>
  • Change mSwitchOutfitHelper.Wait(true) to mSwitchOutfitHelper.Wait(false)


Thank you so much! I changed immediateInteraction to Interaction and the mSwitchOutfitHelper.Wait(true) to mSwitchOutfitHelper.Wait(false). And the code now executes. Thank you!
Field Researcher
Original Poster
#6 Old 25th Aug 2022 at 2:10 AM
Up next I found an event in the game which sounded like it runs each time a sim goes outside or inside. The event is named "kChangedInsideOutsideStatus". Additionally, I found a condition I could place. Such as "IsOutside". Which I then decided to put this info to use to make it so sims remove their shoes whenever they enter their home. And put their shoes on when they leave their home. Here is what I came up with so far.

Code:
private static ListenerAction OnChangedInsideOutsideStatus(Event e)
        {
            try
            {
                if (e.TargetObject is Sim sim)
                {
                    PutShoesOnWhenOutside(sim);
                }
            }
            catch (Exception)
            { }
            return ListenerAction.Keep;
        }

The event I use to run my code whenever it is triggered.

Code:
private static void PutShoesOnWhenOutside(Sim sim)
        {
            if (sim.IsOutside && sim.IsInActiveHousehold)
            {
                if (sim.IsWearingSpecialOutfit(kSpecialOutfitName))
                {
                    sim.SwitchToPreviousOutfitWithoutSpin();
                    sim .SimDescription.RemoveSpecialOutfit(kSpecialOutfitName);
                }
            }
        }

private static string kSpecialOutfitName = "FirewalkBarefoot";


I was unable to use RemoveBarefootOutfit(); that is located in my PutShoesOn Interaction. Meanwhile the code above is in my Instantiator class. However, I was just able to put in the actual code that was in the RemoveBarefootOutfit class.

Code:
 private static void TakeOffShoesIndoors(Sim sim)
        {
            if (sim.IsAtHome && !sim.IsOutside && sim.IsInActiveHousehold);
            {
                
            }
        }


Again, I was unable to use the SetupOutfitSwapIfNeeded(); class. This time I was unable to put in the code that SetOutfitSwapIfNeeded had. It is located in my RemoveShoes interaction class. While the code above is in my Instantiator class.
If I attempt this

Code:
 private static void TakeOffShoesIndoors(Sim sim)
        {
            if (sim.IsAtHome && !sim.IsOutside && sim.IsInActiveHousehold);
            {
                SetupOutfitSwapIfNeeded();
            }
        }


I get an error which says "The name "SetupOutfitSwapIfNeeded' does not exist in the current context." How do I make it in the current context?
Space Pony
#7 Old 25th Aug 2022 at 5:14 AM
Quote: Originally posted by MonocoDoll
I get an error which says "The name "SetupOutfitSwapIfNeeded' does not exist in the current context." How do I make it in the current context?


When a member or method is private, that means it can only be accessed from within the class it was declared in. What you'll want to do is make SetupOutfitSwapIfNeeded() public, which means it can be accessed globally, and static, meaning you don't need an object instance (or any members within that instance) to call it. You'll also need to make all the interaction methods that it calls (i.e. IsWearingShoes and GetBarefootCASPartName) static, but they can remain private unless you need to use them elsewhere.

Then, because these methods are now static, they can no longer use the Actor or mSwitchOutfitHelper members, since those belong to instances of the interaction class. You can fix the former by passing in a Sim as an argument and replacing the occurrences of Actor with that argument, and the latter by just declaring the SwitchOutfitHelper as a local variable within the function.

Also, since these functions are now being used outside of the interaction class, I'd recommend taking them out of that class and putting them in their own static helper class. This isn't required, but it's better design IMO.

The full code of this helper class is below. Note the "using" block, which will automatically call the Dispose() method of switchOutfitHelper once the game exits the block (to avoid dependence on the Cleanup method from the original interaction):



Finally, change your calls to pass in the target Sim, like "Helpers.SetupOutfitSwapIfNeeded(Actor)", and you should be set.

Oh, and one last thing: I'd recommend against using an empty catch block in your event listener. At the very least, you should show the exception in a StyledNotification or SimpleMessageDialog. Otherwise, your code will fail silently and it'd become a lot harder to find the problem.

"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
Field Researcher
Original Poster
#8 Old 26th Aug 2022 at 4:06 AM
I want to say thank you for helping me out. I used the new info to try and make the method to accomplish my goal of sims automatically taking off their shoes when they step inside their house and put their shoes on whenever they step outside.
This is what I have so far.

Code:
 public static class Helpers
    {
        public static void SetUpOutftitSwapIfNeeded(Sim sim)
        {
            if (sim.Service is GrimReaper || !IsWearingShoes(sim))
            {
                return;
            }
            SimDescription simDescription = sim.SimDescription;
            simDescription.RemoveSpecialOutfit(FirewalkPit.Firewalk.kSpecialOutfitName);
            SimBuilder simBuilder = new SimBuilder();
            simBuilder.UseCompression = true;
            OutfitUtils.SetOutfit(simBuilder, sim.CurrentOutfit, simDescription);
            CASPart[] parts = sim.CurrentOutfit.Parts;
            foreach (CASPart part in sim.CurrentOutfit.Parts)
            {
                if (part.BodyType == BodyTypes.Shoes)
                {
                    simBuilder.RemovePart(part);
                }
            }
            string barefootCASPartName = GetBarefootCASPartName(sim);
            ResourceKey resourceKey = new ResourceKey(ResourceUtils.HashString64(barefootCASPartName), 0x34AEECBU, ResourceUtils.ProductVersionToGroupId(ProductVersion.BaseGame));
            if (resourceKey != ResourceKey.kInvalidResourceKey)
            {
                CASPart part2 = new CASPart(resourceKey);
                simBuilder.AddPart(part2);
                ResourceKey key = simBuilder.CacheOutfit("FirewalkPitBarefoot" + simDescription.SimDescriptionId);
                SimOutfit outfit = new SimOutfit(key);
                int num = simDescription.AddSpecialOutfit(outfit, FirewalkPit.Firewalk.kSpecialOutfitName);
                if (num != -1)
                {
                    using (Sim.SwitchOutfitHelper switchOutfitHelper = new Sim.SwitchOutfitHelper(sim, OutfitCategories.Special, num))
                    {
                        switchOutfitHelper.Start();
                        switchOutfitHelper.Wait(false);
                        sim.RefreshCurrentOutfit(false);
                        switchOutfitHelper.ChangeOutfit();
                    }
                }
                return;
            }
        }

        private static bool IsWearingShoes (Sim sim)
        {
            CASPart[] parts = sim.CurrentOutfit.Parts;
            for (int i = 0; i < parts.Length; i++)
            {
                CASPart cASPart = parts[i];
                if (cASPart.BodyType == BodyTypes.Shoes)
                {
                    return !StringUtil.ContainsAny(CASUtils.PartDataGetName(cASPart.Key), "ShoesNude");
                }
            }
            return false;
        }

        private static string GetBarefootCASPartName(Sim sim)
        {
            SimDescription simDescription = sim.SimDescription;
            string text = simDescription.YoungAdult ? "a" : OutfitUtils.GetAgePrefix(simDescription.Age);
            text += OutfitUtils.GetGenderPrefix(simDescription.Gender);
            return text + "ShoesNude";
        }
    }

The above code is my helper class. Which contains the code which takes sims shoes off.
Code:
 private static void PutShoesOnWhenOutside(Sim sim)
        {
            
                if (sim.IsWearingSpecialOutfit(kSpecialOutfitName))
                {
                    sim.SwitchToPreviousOutfitWithoutSpin();
                    sim .SimDescription.RemoveSpecialOutfit(kSpecialOutfitName);
                }
            
        }

        private static string kSpecialOutfitName = "FirewalkBarefoot";


The code above is the code used to put shoes back on to sims who had shoes off.

Code:
private static ListenerAction OnChangedInsideOutsideStatus(Event e)
        {
            try
            {
                if (e.TargetObject is Sim sim)
                {
                    if (!sim.IsOutside && sim.IsInActiveHousehold && sim.IsAtHome)
                    {
                        Helpers.SetUpOutftitSwapIfNeeded(sim);
                    }
                    if (sim.IsOutside && sim.IsInActiveHousehold && !sim.IsAtHome)
                    {
                        PutShoesOnWhenOutside(sim);
                    }
                }
            }
            catch (Exception)
            { }
            return ListenerAction.Keep;
        }

The above code is the event used to execute my code. The event takes effect whenever a sim goes from inside to outside or outside to inside. Then the conditions check whether code should apply to that sim. And if it does then it should have sims take their shoes off whenever they are home. And have them put their shoes off whenever they step outside without their shoes.

However, for some reason the code does not perform this function. I have checked everything that I could possibly think of. First, I checked whether the event kChangedInsideOutsideStatus works as I thought it works. Which it does. I tested this with a pausinator check. So the game would pause any time a sim goes from being inside to outside or reversed. Amd it would pause each time.

The second thing I checked was the conditions. I even removed some of them. And it still did not execute the code to remove and put shoes back on.

Did I do a mistake with my code? The immediate interaction still works flawlessly. It takes off shoes and puts them back on. But, I can't seem to get the automatic method I would like to create to work properly.
Space Pony
#9 Old 26th Aug 2022 at 5:51 AM
Try using e.Actor rather than e.TargetObject. If that doesn't work, I'd try adding StyledNotification.Show() at various points in your code, to see if there's a point where the game is stopping execution early, before the outfit is actually changed. Maybe put a couple before the calls to PutShoesOneWhenOutside() or SetUpOutfitSwapIfNeeded() to start with. If you see notifications showing up in-game, then move them into the helper functions until you find the point where they stop appearing.

"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
Field Researcher
Original Poster
#10 Old 26th Aug 2022 at 4:22 PM
Quote: Originally posted by gamefreak130
Try using e.Actor rather than e.TargetObject. If that doesn't work, I'd try adding StyledNotification.Show() at various points in your code, to see if there's a point where the game is stopping execution early, before the outfit is actually changed. Maybe put a couple before the calls to PutShoesOneWhenOutside() or SetUpOutfitSwapIfNeeded() to start with. If you see notifications showing up in-game, then move them into the helper functions until you find the point where they stop appearing.


This is a little embarrassing, but could you give me a small example of how to set up the StyledNotification.Show(). This is what I have tried as an example, but I get the error Argument 1: cannot convert from 'string' to 'Sims3.UI.StyledNotification.Format'

Code:
 private static void PutShoesOnWhenOutside(Sim sim)
        {
            
                if (sim.IsWearingSpecialOutfit(kSpecialOutfitName))
                {
                StyledNotification.Show("Testing Example");
                    sim.SwitchToPreviousOutfitWithoutSpin();
                    sim .SimDescription.RemoveSpecialOutfit(kSpecialOutfitName);
                }
            
        }
Space Pony
#11 Old 26th Aug 2022 at 11:04 PM
Here's one way to do it, you'll get a big stop sign as the icon so you know it's a debug message:

Code:
StyledNotification.Show(new StyledNotification.Format("Testing Example", StyledNotification.NotificationStyle.kDebugAlert));

You could also do what @Lyralei does and make your own print function to save yourself some keystrokes:

Code:
public static void print(string entry)
{
   StyledNotification.Show(new StyledNotification.Format(entry, StyledNotification.NotificationStyle.kDebugAlert));
}

print("Testing Example");

"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
Field Researcher
Original Poster
#12 Old 27th Aug 2022 at 4:32 AM
I tested the Helpers SetUpOutfitSwapIfNeeded class just now. The notification fails to appear after the code "using (Sim.SwitchOutfitHelper switchOutfitHelper = new Sim.SwitchOutfitHelper(sim, OutfitCategories.Special, num))" Which if it were to run then I would receive a notification saying SetUpOutfitSwapIfNeeded5. I did receive the notification for the other 4 notifcations. What could be stopping the rest of the code from running?

Code:
        public static void SetUpOutftitSwapIfNeeded(Sim sim)
        {
            if (sim.Service is GrimReaper || !IsWearingShoes(sim))
            {
                StyledNotification.Show(new StyledNotification.Format("Testing Example For GrimReaper/IsWearingShoes Condition", StyledNotification.NotificationStyle.kDebugAlert));
                return;
            }
            SimDescription simDescription = sim.SimDescription;
            simDescription.RemoveSpecialOutfit(FirewalkPit.Firewalk.kSpecialOutfitName);
            SimBuilder simBuilder = new SimBuilder();
            simBuilder.UseCompression = true;
            OutfitUtils.SetOutfit(simBuilder, sim.CurrentOutfit, simDescription);
            CASPart[] parts = sim.CurrentOutfit.Parts;
            StyledNotification.Show(new StyledNotification.Format("SetUpOutfitSwapIfNeeded 1", StyledNotification.NotificationStyle.kDebugAlert));
            foreach (CASPart part in sim.CurrentOutfit.Parts)
            {
                if (part.BodyType == BodyTypes.Shoes)
                {
                    simBuilder.RemovePart(part);
                    StyledNotification.Show(new StyledNotification.Format("SetUpOutfitSwapIfNeeded 2", StyledNotification.NotificationStyle.kDebugAlert));
                }
            }
            string barefootCASPartName = GetBarefootCASPartName(sim);
            ResourceKey resourceKey = new ResourceKey(ResourceUtils.HashString64(barefootCASPartName), 0x34AEECBU, ResourceUtils.ProductVersionToGroupId(ProductVersion.BaseGame));
            if (resourceKey != ResourceKey.kInvalidResourceKey)
            {
                CASPart part2 = new CASPart(resourceKey);
                simBuilder.AddPart(part2);
                ResourceKey key = simBuilder.CacheOutfit("FirewalkPitBarefoot" + simDescription.SimDescriptionId);
                SimOutfit outfit = new SimOutfit(key);
                int num = simDescription.AddSpecialOutfit(outfit, FirewalkPit.Firewalk.kSpecialOutfitName);
                if (num != -1)
                    StyledNotification.Show(new StyledNotification.Format("SetUpOutfitSwapIfNeeded 3", StyledNotification.NotificationStyle.kDebugAlert));
                {
                    StyledNotification.Show(new StyledNotification.Format("SetUpOutfitSwapIfNeeded 4", StyledNotification.NotificationStyle.kDebugAlert));
                    using (Sim.SwitchOutfitHelper switchOutfitHelper = new Sim.SwitchOutfitHelper(sim, OutfitCategories.Special, num))
                    {
                        switchOutfitHelper.Start();
                        switchOutfitHelper.Wait(false);
                        sim.RefreshCurrentOutfit(false);
                        switchOutfitHelper.ChangeOutfit();
                        StyledNotification.Show(new StyledNotification.Format("SetUpOutfitSwapIfNeeded 5", StyledNotification.NotificationStyle.kDebugAlert));
                    }
                }
                return;
            }
Space Pony
#13 Old 27th Aug 2022 at 6:18 AM
Most likely an exception of some kind is being thrown. Remember when I said you shouldn't use an empty catch block in your event handler? Doing that effectively silences the exception, which means we can't find out what's going wrong. Getting error messages is better than failing silently, because it gives a message and stack trace that can (mostly) show what happened.

Replace your catch block with this:

Code:
catch (Exception e)
{
    uint fileHandle = 0;
    try
    {
        Simulator.CreateExportFile(ref fileHandle, "ExceptionLog__");
        if (fileHandle != 0)
        {
            CustomXmlWriter xmlWriter = new CustomXmlWriter(fileHandle);
            xmlWriter.WriteStartDocument();
            xmlWriter.WriteToBuffer(e.ToString());
            xmlWriter.FlushBufferToFile();
        }
    }
    finally
    {
        if (fileHandle != 0)
        {
            Simulator.CloseScriptErrorFile(fileHandle);
        }
    }
    StyledNotification.Show(new StyledNotification.Format("Exception Logged", StyledNotification.NotificationStyle.kDebugAlert));
}


This code should notify you if an exception occurred and write its message and stack trace to an XML file in your user data (C:\Users\[UserName]\Documents\Electronic Arts\The Sims 3) beginning with "ExceptionLog". If it does, open that XML file in a text editor and post the contents here, preferably formatted using the [CODE] tags.

"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
Field Researcher
Original Poster
#14 Old 27th Aug 2022 at 9:37 PM
Here is what I got after I added the code to my catch block.
Code:
<?xml version="1.0" encoding="utf-8"?>
Sims3.Gameplay.EventSystem.Event
Here is how I inputted the code into my event.
Code:
 private static ListenerAction OnChangedInsideOutsideStatus(Event e)
        {
            try
            {
                if (e.Actor is Sim sim)
                {
                    
                    
                    if (!sim.IsOutside && sim.IsInActiveHousehold && sim.IsAtHome)
                    {
                        
                        Helpers.SetUpOutftitSwapIfNeeded(sim);
                    }
                    if (sim.IsOutside && sim.IsInActiveHousehold)
                    {
                       
                        PutShoesOnWhenOutside(sim);
                    }
                }
            }
            catch (Exception)
            {
                uint fileHandle = 0;
                try
                {
                    Simulator.CreateExportFile(ref fileHandle, "ExceptionLog__");
                    if (fileHandle != 0)
                    {
                        CustomXmlWriter xmlWriter = new CustomXmlWriter(fileHandle);
                        xmlWriter.WriteStartDocument();
                        xmlWriter.WriteToBuffer(e.ToString());
                        xmlWriter.FlushBufferToFile();
                    }
                }
                finally
                {
                    if (fileHandle != 0)
                    {
                        Simulator.CloseScriptErrorFile(fileHandle);
                    }
                }
                StyledNotification.Show(new StyledNotification.Format("Exception Logged", StyledNotification.NotificationStyle.kDebugAlert));
            }
            return ListenerAction.Keep;
        }
Space Pony
#15 Old 27th Aug 2022 at 10:06 PM
Quote: Originally posted by MonocoDoll
Here is what I got after I added the code to my catch block.


You are exporting the ToString result of your event which jsut returns its type as string you need to export your exception which you are not chatching at the moment

Space Pony
#16 Old 27th Aug 2022 at 11:04 PM
Quote: Originally posted by Battery
You are exporting the ToString result of your event which jsut returns its type as string you need to export your exception which you are not chatching at the moment


Yep, sorry, forgot that "e" is the variable for the Event you're responding to.

I'd still recommend using "exc.ToString()" though, so that you can log the stack trace as well.

"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
Field Researcher
Original Poster
#17 Old 27th Aug 2022 at 11:18 PM
Here is what I got this time.

Code:
<?xml version="1.0" encoding="utf-8"?>
Attempting to yield in a non-yielding context!


Just incase I did an error on my part. Here is how I added the code into the event.

Code:
private static ListenerAction OnChangedInsideOutsideStatus(Event e)
        {
            try
            {
                if (e.Actor is Sim sim)
                {
                    
                    
                    if (!sim.IsOutside && sim.IsInActiveHousehold && sim.IsAtHome)
                    {
                        
                        Helpers.SetUpOutftitSwapIfNeeded(sim);
                    }
                    if (sim.IsOutside && sim.IsInActiveHousehold)
                    {
                       
                        PutShoesOnWhenOutside(sim);
                    }
                }
            }
            catch (Exception exc)
            {
                uint fileHandle = 0;
                try
                {
                    Simulator.CreateExportFile(ref fileHandle, "ExceptionLog__");
                    if (fileHandle !=0)
                    {
                        CustomXmlWriter xmlWriter = new CustomXmlWriter(fileHandle);
                        xmlWriter.WriteStartDocument();
                        xmlWriter.WriteToBuffer(exc.Message);
                        xmlWriter.FlushBufferToFile();
                    }
                }
                finally
                {
                    if (fileHandle !=0)
                    {
                        Simulator.CloseScriptErrorFile(fileHandle);
                    }
                }
                StyledNotification.Show(new StyledNotification.Format("Exception Logged", StyledNotification.NotificationStyle.kDebugAlert));
                
            }
            return ListenerAction.Keep;
        }
Space Pony
#18 Old 28th Aug 2022 at 3:51 AM
Quote: Originally posted by MonocoDoll
Here is what I got this time.

Code:
<?xml version="1.0" encoding="utf-8"?>
Attempting to yield in a non-yielding context!


Just incase I did an error on my part. Here is how I added the code into the event.


No error on your part, that's exactly what we needed. However, that's... not the kind of exception I was expecting

Try wrapping your call to SetUpOutftitSwapIfNeeded in a Simulator object, like so:

Code:
Simulator.AddObject(new OneShotFunctionTask(() => Helpers.SetUpOutftitSwapIfNeeded(sim)));

That should place the code into the "yielding context" it's expecting, hopefully without any other side effects. I don't think you need to do this for the call to PutShoesOnWhenOutside, but I would test it first to make sure.

"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
Field Researcher
Original Poster
#19 Old 28th Aug 2022 at 7:20 AM
Thank you so much for the solution. The sim can now automitcally enter their home and remove their shoes. And they will also put on their shoes when they go outside automitcally.

I did find one bug in my code. If I were to use the immediate interaction to remove the sims shoes, and then have them enter their home barefoot. Then the sim would become invisible. However, after some thinking. I found an easy fix. Just make a condition that would cancel the event interaction trigger if the sim were to have a special outfit on. Hence, I used !sim.IsWearingSpecialOutfit(kSpecialOutfitName) for one of my conditions.

Code:
 private static ListenerAction OnChangedInsideOutsideStatus(Event e)
        {
            try
            {
                if (e.Actor is Sim sim)
                {


                    if (!sim.IsOutside && sim.IsInActiveHousehold && sim.IsAtHome && !sim.IsWearingSpecialOutfit(kSpecialOutfitName))
                    {
                        
                      Simulator.AddObject(new OneShotFunctionTask(() => Helpers.SetUpOutftitSwapIfNeeded(sim)));
                    }


At first, I was gonna use a buff condition in order to cancel the event if I were to have the sim already barefoot, but then I remembered the specialoutfit condition lol.

Now all that is left is to make it dependent on an object. I have practiced adding interactions to objects in the past, so I know I can do it. However, I am very curious if it is possible to make the events such as kChangedInsideOutsideStatus kind of like an off and on switch for an object? For example, what I have in mind is to use the shoe rack object in TS3. Then I can make two interactions. One to remove shoes while at home for the activehousehold. Which this interaction from the shoe rack would then turn on the event listener for kChangedInsideOutsideStatus. Which would then run the code to remove and add shoes to sims when they go inside or outside. And, then the second interaction would be "Shoes while Indoors". which would turn off or dispose the event listener for kChangedInsideOutsideStatus, and it would disable the code to remove shoes automatically when entering your home.

At the moment, I can't think of an object in game with a similar method. The only thing I had in mind was the autolight's function. I thought they may have used the kRoomChanged event when you use the autolights interaction, but I skimmed through the code, and could not find it in use. I'll continue to try to look for something ingame with a similar function. Again thank you for everything!
Lab Assistant
#20 Old 28th Aug 2022 at 2:52 PM
Quote: Originally posted by MonocoDoll
At the moment, I can't think of an object in game with a similar method. The only thing I had in mind was the autolight's function. I thought they may have used the kRoomChanged event when you use the autolights interaction, but I skimmed through the code, and could not find it in use. I'll continue to try to look for something ingame with a similar function. Again thank you for everything!


If I were you, I'd take a peek at how consort's dresscode script handles this.
Field Researcher
Original Poster
#21 Old 29th Aug 2022 at 2:39 AM
Quote: Originally posted by aarin
If I were you, I'd take a peek at how consort's dresscode script handles this.


Thank you so much for your suggestion. After looking at Consorts code all day today, I was able to put something together to make it work as I wanted it to. Thank you!! Now I just need to make more conditional options, and I might be set for this project.
Field Researcher
Original Poster
#22 Old 10th Sep 2022 at 5:47 AM
Hello, I was made aware of a bug regarding my Remove Shoes indoors script mod. And I believe I found the diagnosis.

The bug effects children/toddlers. Whenever the remove shoes method runs for a child or a toddler sim, the feet end up turning invisible, rather than just removing the shoes and keeping the feet. So, after looking at my code and trying to figure out what may cause this. I came to the conclusion that the reason it does not work is because first the firewalk object cannot be used by children/toddlers. This is relevant, because the original method that removes the shoes relies on a specialoutfit. That specialoutfit being the FirewalkPitBarefoot. And if children/toddler sims cannot use the firewalk object, then a special outfit varient for the FirewalkPitBarefoot was never made for children/toddlers.

So, I then decided to try to make a seperate method to apply the barefoot feet for children/toddlers. A method which would not rely on special outfits. However, I am at a loss on how to make such a method at the moment. I will look more into it tomorrow. As I believe I may be able to figure out a method if I look at Consorts code and the code provided by gamefreak130. Again any help would be appreciated, thank you.
Space Pony
#23 Old 10th Sep 2022 at 4:19 PM
Quote: Originally posted by MonocoDoll
Hello, I was made aware of a bug regarding my Remove Shoes indoors script mod. And I believe I found the diagnosis.

The bug effects children/toddlers. Whenever the remove shoes method runs for a child or a toddler sim, the feet end up turning invisible, rather than just removing the shoes and keeping the feet. So, after looking at my code and trying to figure out what may cause this. I came to the conclusion that the reason it does not work is because first the firewalk object cannot be used by children/toddlers. This is relevant, because the original method that removes the shoes relies on a specialoutfit. That specialoutfit being the FirewalkPitBarefoot. And if children/toddler sims cannot use the firewalk object, then a special outfit varient for the FirewalkPitBarefoot was never made for children/toddlers.

So, I then decided to try to make a seperate method to apply the barefoot feet for children/toddlers. A method which would not rely on special outfits. However, I am at a loss on how to make such a method at the moment. I will look more into it tomorrow. As I believe I may be able to figure out a method if I look at Consorts code and the code provided by gamefreak130. Again any help would be appreciated, thank you.


The invisible feet is probably due to the Sims changing into a nonexistent CAS part. The GetBarefootPartCASName() method assumes that there are separate barefoot CAS parts for males and females, but this is true only for teens, adults, or elders (the only ages that can use the firewalk pit). Children and toddlers each have a single unisex barefoot outfit with a slightly different naming scheme, so with children, for example, the method will return the nonexistent "cfShoesNude" or "cmShoesNude" as the parts to change into, rather than using the correct part name of "cuShoesNude" for both genders.

Luckily, it's a pretty easy change to account for this. The added code is in bold:

Code:
public string GetBarefootCASPartName()
{
    SimDescription simDescription = this.Actor.SimDescription;
    string s = simDescription.YoungAdult ? "a" : OutfitUtils.GetAgePrefix(simDescription.Age);
    s += simDescription.ChildOrBelow ? "u" : OutfitUtils.GetGenderPrefix(simDescription.Gender);
    return s + "ShoesNude";
}

"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
Field Researcher
Original Poster
#24 Old 10th Sep 2022 at 8:45 PM
Thank you so much. It worked. I can't believe it was that simple. I legit was going to try to make an alternative method to remove shoes for children/toddlers. You are the best!
Back to top