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!
Virtual gardener
staff: administrator
#76 Old 17th Jan 2021 at 11:26 AM
Quote: Originally posted by Jathom95
Just my .2 cents, but I'd say for balance (pun intended ) just go with tranquil. It's already pretty underutilized as it is, so it would be a good alternative to taking bubble baths all the time. I haven't played around in Shang Simla much, but it sounds like the Zen moodlet may be one of those special ones, so it might possibly be weighted slightly more than tranquil, though I'm not entirely sure on that one.

Plus, drawing from real life influence, yoga is a mind and body exercise so tranquil fits better I think. The same way for taking a bubble bath, because you'd be relieving stress on your body and mind. Zen is more or less a state of mind, without getting into semantics, it's more about your mental state and having a clear head, so it really fits better for meditation, where (I think) it's already used by EA.
And! it makes it more Base game friendly
Advertisement
Lab Assistant
Original Poster
#77 Old 18th Jan 2021 at 6:39 AM Last edited by Alunn : 18th Jan 2021 at 7:22 AM.
Quote: Originally posted by Jathom95
Just my .2 cents, but I'd say for balance (pun intended ) just go with tranquil. It's already pretty underutilized as it is, so it would be a good alternative to taking bubble baths all the time. I haven't played around in Shang Simla much, but it sounds like the Zen moodlet may be one of those special ones, so it might possibly be weighted slightly more than tranquil, though I'm not entirely sure on that one.

Plus, drawing from real life influence, yoga is a mind and body exercise so tranquil fits better I think. The same way for taking a bubble bath, because you'd be relieving stress on your body and mind. Zen is more or less a state of mind, without getting into semantics, it's more about your mental state and having a clear head, so it really fits better for meditation, where (I think) it's already used by EA.

I agree that tranquil is very underutilized, despite it packing a good punch in elevating a sim's mood. Plus the real life comparisons you made make a lot of sense. Tranquil it is!
Quote: Originally posted by Lyralei
And! it makes it more Base game friendly

That too!

I also tried taking a stab at adding the function of practicing until tranquil from the bathtub, but the section "Target.YogaMat.MinsToAddTranquil" is giving the following error:

Severity Code Description Project File Line Suppression State
Error CS1061 'YogaMat' does not contain a definition for 'YogaMat' and no accessible extension method 'YogaMat' accepting a first argument of type 'YogaMat' could be found (are you missing a using directive or an assembly reference?)

Any ideas on how to fix it? This is the code:

Code:
namespace Sims3.Gameplay.Objects.Alunn
{
    public class YogaMat : GameObject
    {
        public class PracticeYoga : Interaction<Sim, YogaMat>
        {
            public class Definition : InteractionDefinition<Sim, YogaMat, PracticeYoga>
            {
                public override string GetInteractionName(Sim actor, YogaMat target, InteractionObjectPair interaction)
                {
                    return "Practice Yoga";
                }
                public override bool Test(Sim actor, YogaMat target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    return true;
                }
            }
            public static InteractionDefinition Singleton = new Definition();

            public BuffTimedStage mBuffTimedStageTranquil;

            public override void ConfigureInteraction()
            {
                base.ConfigureInteraction();
                mBuffTimedStageTranquil = new BuffTimedStage(Localization.LocalizeString("Gameplay/Objects/YogaMat/PracticeYoga:PracticeYogaUntilTranquilStage"), Target.TuningBathtub.MinsToAddTranquil, showCompletionTime: false, selectable: true, visibleProgress: true, BuffNames.Tranquil);
                mStages.Add(mBuffTimedStageTranquil);
                base.ActiveStage = mBuffTimedStageTranquil;
            }
            public override bool Run()
            {
                if (!Actor.RouteToSlot(Target, (Slot)0x31229A4C))
                {
                    Actor.PlayRouteFailure();
                    return false;
                }
                // We ask the game to give/initiate the skill on the actor that's currently using the object. Even if they're level 10 or don't know the skill yet, this will be added. 
                Athletic athleticSkill = base.Actor.SkillManager.AddElement(SkillNames.Athletic) as Athletic;
                if (athleticSkill == null)
                {
                    // For good practise on a first run, make sure to add an in-game notification here to debug whether the skill has been loaded or not
                    return false;
                }
                // Starts skill gaining. The Skilling bar will appear above the sim's head now.
                athleticSkill.StartSkillGain(2);
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.Actor.SwitchToOutfitWithSpin(Sim.ClothesChangeReason.GoingToWorkOut, OutfitCategories.Athletic);
                base.SetActor("YogaMat_object", base.Target);
                base.Actor.RouteToSlot(this.Target, (Slot)0x31229A4C);
                base.EnterState("x", "Enter");
                base.SetParameter("skill", (object)Actor.SkillManager.GetSkillLevelParameterForJazzGraph(SkillNames.Athletic));
                base.BeginCommodityUpdates();
                base.AnimateSim("Loop");
                // EA uses the 'Flag' variable for this. But when looping, it's important on each loop to return either 'true' or 'false'. An exit reason can return a false or true 
                bool GainSkillOnLoop = DoLoop(ExitReason.Default, LoopInfinite, null);
                base.AnimateSim("Exit");
                base.EndCommodityUpdates(true);
                // Because we're exiting, we're also stopping skillgaining here.
                athleticSkill.StopSkillGain();
                base.StandardExit();
                // This is the only instance in a run() function to return a variable like this. Merely because of the 'DoLoop' function.
                return GainSkillOnLoop;

            }
            // Our loop function. See how in: bool GainSkillOnLoop = DoLoop(ExitReason.Default, LoopInfinite, null); We call a function in the second parameter? 
            public override bool DoLoop(ExitReason loopExitReasons, InsideLoopFunction loopMe, StateMachineClient smc)
            {
                bool flag = base.DoLoop(loopExitReasons, loopMe, smc);
                if (flag && base.ActiveStage == mBuffTimedStageTranquil)
                {
                    Actor.BuffManager.AddElementPaused(BuffNames.Tranquil, Origin.FromBubbleBath);
                }
                return flag;
            }
            public void LoopInfinite(StateMachineClient smc, LoopData loopData)
            {
                //2 things here, if either we as the player cancel the animation OR the game notices that the sim is super hungry/has no fun/has to go to the toilet, we'll add the exit reasons. 
                if (Actor.HasExitReason(ExitReason.UserCanceled) || (Actor.HasExitReason(ExitReason.MoodFailure)))
                {
                    // AddExitReason so that we 'base.AnimateSim()' will be initiated
                    base.Actor.AddExitReason(ExitReason.UserCanceled);
                    base.Actor.AddExitReason(ExitReason.MoodFailure);
                }
            }
        }
Senior Moderator
staff: senior moderator
#78 Old 18th Jan 2021 at 3:52 PM
So the section I think you mean is "Target.TuningBathtub.MinsToAddTranquil" that the bath uses, actually is an integer value. TuningBathtub I think must refer to the object tuning that's determined by the different types of bathtub eg one of the cheap bathtubs, and then the MinsToAddTranquil is the number specifically referring to how long it takes to get tranquil. So for cheaper bathtubs it might take longer, and the values are all defined in the tuning xmls for each bath.

The reason you can't use that line of code is because your yoga mat doesn't have that info defined.

So what you can do, is either hard-code a value, like 60 sim minutes, or make a tunable one.
If you want to make a tunable value, I think the easiest way is to just use a static integer variable in the yogamat or interaction's class, use [Tunable] above it, and then make an xml with the variable just like you'd do with pure modding with the kInstantiator variable, and with the xml name being the namespace.class of where the variable is. Then the value can be changed in the xml or with nraas Retuner I think.

So for example, in the YogaMat class, you could just have a variable:
Code:
[Tunable]
public static int kMinsUntilTranquil = 60;
and then in ConfigureInteraction():

Code:
mBuffTimedStageTranquil = new BuffTimedStage(Localization.LocalizeString("Gameplay/Objects/YogaMat/PracticeYoga:PracticeYogaUntilTranquilStage"), kMinsUntilTranquil, showCompletionTime: false, selectable: true, visibleProgress: true, BuffNames.Tranquil);
Note you don't need Target.kMinsUntilTranquil if you make the variable static, which means it's just like an independent (?) variable, rather than being tied to a specific object, like the Target YogaMat in this case. You could make it not static, but it's more of a faff, and unless you want like, different quality yoga mats with different tunings, there's probably no point :P
I hope my rubbish explanation of static variables makes some sort of sense, honestly I don't even know all the object-oriented programming lingo :P
Lab Assistant
Original Poster
#79 Old 19th Jan 2021 at 2:04 AM Last edited by Alunn : 19th Jan 2021 at 2:27 AM.
I think I got most of what you said. I'm just a little confused on the part about the xml and kInstantiator, as I've never done anything with those before. I'm currently reading through this tutorial and it looks like I have to copy and then edit one from the game. Do I just pick one that has the kInstantiator variable or use the one from the bathtub? I'll also take a look at Buzzler's pure script modding tutorial.

Edit: Now my sims can't select the Practice Yoga interaction... Back to the drawing board on that. I also made this _xml file with the Sims3.Gameplay.Objects.Alunn.YogaMat namespace.
<?xml version="1.0" encoding="utf-8"?>
<base>
<Current_Tuning>
<kMinsUntilTranquil value="60" />
</Current_Tuning>
</base>
Virtual gardener
staff: administrator
#80 Old 19th Jan 2021 at 10:23 AM
I do wanna pick up a little on the  CS1061 errors. (So: https://docs.microsoft.com/en-us/do...messages/cs1061) and which variables need a target though, since I don't feel tutorials exactly talk about this much. So this is more or less for the people stumbling across this.

In most cases, we don't need to make things static like zoe said. I noticed that the sewing table actually suffers from a very fatal bug, which is that with object modding, target literally means only *that* particular object you're currently clicking on, on the world holds the information. So, a mod I'm currently working on is a great example of this. Say we have a journal object mod, and inside our code we have a list that holds strings being the entries of the journal.

If we called this list inside our interaction like so:

Code:
Target.SavedEntries.Add("Hello i'm an entry!");
Then, it would be ONLY saved for that one particular journal. Any other journals in the world won't hold this particular information.

If we did this instead:

Code:
SavedEntries.Add("Hello!");
Then EVERY journal in the world would have this sim's entry in their journals. This is pretty bad for obvious reasons  But indeed, for *tuning* values that don't change per object (or even, say, the 'duration' of an animation) then it should be fine. You can of course do it but it's indeed not mandatory.

------

Quote:
Edit: Now my sims can't select the Practice Yoga interaction... Back to the drawing board on that. I also made this _xml file with the Sims3.Gameplay.Objects.Alunn.YogaMat namespace.

With selecting, do you mean that you see it in the pie menu but they exit after you click it? Or it simply doesn't show up? I think after you fixed that, it will be easier to test the tuning. I think the tuning stuff should be fine!
Senior Moderator
staff: senior moderator
#81 Old 19th Jan 2021 at 11:56 AM
Quote: Originally posted by Alunn
I think I got most of what you said. I'm just a little confused on the part about the xml and kInstantiator, as I've never done anything with those before. I'm currently reading through this tutorial and it looks like I have to copy and then edit one from the game. Do I just pick one that has the kInstantiator variable or use the one from the bathtub? I'll also take a look at Buzzler's pure script modding tutorial.
Ohh sorry, for some reason I thought you'd done pure modding for part of this :P
Basically to get a pure mod to work, you have to make a tunable variable which is a sneaky way to get the game to read your code, Buzzler explains a bit about it like it's getting your "foot in the door".
And so to make a tunable value that you actually use the value of, it works in the same way as that. 
So you have your [Tunable] variable declared in the class, and then you make an xml resource just like in the pure modding tutorial, with the instance name as the location of the variable, so namespace.class like you did, and have your variable inside. Then if you change the value of the variable in the xml file, the game will use that rather than whatever is defined in the code itself, thus making a tunable value

You also need to add [assembly: Tunable] in AssemblyInfo.cs to get tunable values to work, and that's all in Buzzler's tutorial.

I don't think the xml modding tutorial will help you there because that is for editing the tunable values already in game, whereas you want to create a variable that can be tuned.

And Lyralei's explanation on static/object oriented stuff is really great :P
So if this value is always going to be the same across all PracticeYoga interactions, and all YogaMats using it, it should be fine to keep it static (I'm glad I was right there because I never really know :P). Whereas if it were to be different based on something to do with the yoga mat or the interaction itself, you'd need it to be "attached" to the object or interaction. Which you can do, but tunable values have to be static I think, so you'd have to assign the non-static variable the value of the static tunable one, which is just more effort if you don't need to :P
And I feel like I've made it more confusing...

But hopefully once the tunable variable is set up properly, the interaction should work again? If nothing else has changed then I don't see why it wouldn't work, I imagine that it's just messing up if there is an error with the xml or something...
Lab Assistant
Original Poster
#82 Old 20th Jan 2021 at 6:57 AM
Quote: Originally posted by Lyralei
I do wanna pick up a little on the  CS1061 errors. (So: https://docs.microsoft.com/en-us/do...messages/cs1061) and which variables need a target though, since I don't feel tutorials exactly talk about this much. So this is more or less for the people stumbling across this.

In most cases, we don't need to make things static like zoe said. I noticed that the sewing table actually suffers from a very fatal bug, which is that with object modding, target literally means only *that* particular object you're currently clicking on, on the world holds the information. So, a mod I'm currently working on is a great example of this. Say we have a journal object mod, and inside our code we have a list that holds strings being the entries of the journal.

If we called this list inside our interaction like so:

Code:
Target.SavedEntries.Add("Hello i'm an entry!");
Then, it would be ONLY saved for that one particular journal. Any other journals in the world won't hold this particular information.

If we did this instead:

Code:
SavedEntries.Add("Hello!");
Then EVERY journal in the world would have this sim's entry in their journals. This is pretty bad for obvious reasons  But indeed, for *tuning* values that don't change per object (or even, say, the 'duration' of an animation) then it should be fine. You can of course do it but it's indeed not mandatory.

------


With selecting, do you mean that you see it in the pie menu but they exit after you click it? Or it simply doesn't show up? I think after you fixed that, it will be easier to test the tuning. I think the tuning stuff should be fine!

Yes, the interaction is still in the pie menu. However, when I click on it nothing happens. It doesn't even get added to the queue. The only changes I made are what I listed in my previous post, but if I had to guess on what went wrong, I think it has something to do with the base.ConfigureInteraction(); section. But I don't know if I can remove that section and still make the interaction work like the bathtub.

I went ahead and removed the Target. section and filled in with the kMinsUntilTranquil valuable 60, so now it looks like this:

Code:
public static InteractionDefinition Singleton = new Definition();

            public BuffTimedStage mBuffTimedStageTranquil;

            public static int kMinsUntilTranquil = 60;

            public override void ConfigureInteraction()
            {
                base.ConfigureInteraction();
                mBuffTimedStageTranquil = new BuffTimedStage(Localization.LocalizeString("Gameplay/Objects/YogaMat/PracticeYoga:PracticeYogaUntilTranquilStage"), 60, showCompletionTime: false, selectable: true, visibleProgress: true, BuffNames.Tranquil);
                mStages.Add(mBuffTimedStageTranquil);
                base.ActiveStage = mBuffTimedStageTranquil;
            }
Senior Moderator
staff: senior moderator
#83 Old 20th Jan 2021 at 11:37 AM Last edited by zoe22 : 20th Jan 2021 at 11:49 AM.
I'm not sure if this would cause the issue, but the part "Localization.LocalizeString("Gameplay/Objects/YogaMat/PracticeYoga:PracticeYogaUntilTranquilStage")" won't actually work unless you've set up your STBL for localising your strings, which I don't think you have as the interaction names are just hardcoded.

So for now you could just replace that with a hardcoded string like "Practice until tranquil". I doubt that this was stopping the interaction working, because I feel like it would just showup blank instead but it's something that needs to be fixed :P

I'll have a closer look at the bath code to see if I can see anything that might be missing... 

edit: okay so in the Take Bubble Bath interaction, base.ConfigureInteraction() actually is referring to the Take Bath interaction which has an extra thing about stages:
Code:
 public override void ConfigureInteraction()
        {
            float bathTime = GetBathTime();
            mBathStage = new TimedStage(Localization.LocalizeString("Gameplay/Objects/Plumbing/Bathtub/TakeBath:BatheUntilCleanStage"), bathTime, showCompletionTime: false, selectable: true, visibleProgress: true);
            base.Stages = new List<Stage>(new Stage[1]
            {
                mBathStage
            });
        }
I'm not sure exactly what you need to get it working, maybe you need to add the YogaUntilTranquil state to base.Stages like the regular bath stage is added? So copy that last bit from the regular bath interaction to the end of yours, and instead of mBathStage, put mBuffTimedStageTranquil? Or I'm not sure if you also need a stage for the regular version...
Maybe Lyralei can help there 
Virtual gardener
staff: administrator
#84 Old 22nd Jan 2021 at 1:33 PM
Try doing this:

Code:
        public override void ConfigureInteraction()
        {
            base.ConfigureInteraction();
            mBuffTimedStageTranquil = new BuffTimedStage("BatheTillTranquil", 60f, false, true, true, BuffNames.Tranquil);
            base.mStages.Add(mBuffTimedStageTranquil);
            base.ActiveStage = mBuffTimedStageTranquil;
        }
Now, if that doesn't work, do this:

Code:

public override bool run()
{
               TimedStage mBuffTimedStageTranquil = new BuffTimedStage("BatheTillTranquil", 60f, false, true, true, BuffNames.Tranquil);
                base.Stages = new List<Stage>(new Stage[1]
                {
                    mBuffTimedStageTranquil
                });
   
}
         public override bool DoLoop(ExitReason loopExitReasons, InsideLoopFunction loopMe, StateMachineClient smc)
        {
         // I think you can do it without this flag, but this is EA's code so I guess we should give them the benefit of the doubt :p
            bool flag = base.DoLoop(loopExitReasons, loopMe, smc);
            if (flag && base.ActiveStage == mBuffTimedStageTranquil)
            {
                base.Actor.BuffManager.AddElementPaused(BuffNames.Tranquil, Origin.FromBubbleBath);
            }
            return flag;
        }
I will say the second option is what I usually do :p So it's a bit like Zoe's code, but in the run function. If you do have a STBL key though, i'd say, use that rather than a hardcoded string. Otherwise this is allright for debugging
Lab Assistant
Original Poster
#85 Old 24th Jan 2021 at 4:15 AM
Quote: Originally posted by Lyralei
Try doing this:

Code:
        public override void ConfigureInteraction()
        {
            base.ConfigureInteraction();
            mBuffTimedStageTranquil = new BuffTimedStage("BatheTillTranquil", 60f, false, true, true, BuffNames.Tranquil);
            base.mStages.Add(mBuffTimedStageTranquil);
            base.ActiveStage = mBuffTimedStageTranquil;
        }
Now, if that doesn't work, do this:

Code:

public override bool run()
{
               TimedStage mBuffTimedStageTranquil = new BuffTimedStage("BatheTillTranquil", 60f, false, true, true, BuffNames.Tranquil);
                base.Stages = new List<Stage>(new Stage[1]
                {
                    mBuffTimedStageTranquil
                });
   
}
         public override bool DoLoop(ExitReason loopExitReasons, InsideLoopFunction loopMe, StateMachineClient smc)
        {
         // I think you can do it without this flag, but this is EA's code so I guess we should give them the benefit of the doubt :p
            bool flag = base.DoLoop(loopExitReasons, loopMe, smc);
            if (flag && base.ActiveStage == mBuffTimedStageTranquil)
            {
                base.Actor.BuffManager.AddElementPaused(BuffNames.Tranquil, Origin.FromBubbleBath);
            }
            return flag;
        }
I will say the second option is what I usually do :p So it's a bit like Zoe's code, but in the run function. If you do have a STBL key though, i'd say, use that rather than a hardcoded string. Otherwise this is allright for debugging

Thank you. I used the second option and the interaction is working again; the bar for filling up the time spent in the interaction is also showing up. The only issue now is that the bar never fills. Do I need to add a new stage somehow or would that be unnecessary?
Virtual gardener
staff: administrator
#86 Old 25th Jan 2021 at 9:35 AM
Quote: Originally posted by Alunn
Thank you. I used the second option and the interaction is working again; the bar for filling up the time spent in the interaction is also showing up. The only issue now is that the bar never fills. Do I need to add a new stage somehow or would that be unnecessary?
From what I've noticed in my own projects, that seems to only happen if you haven't specified yet what the process is

I think in your case (but i've never tried this myself tbh) You could just add this at the bottom of when you add it to the stages list:

Code:
base.ActiveStage = mBuffTimedStageTranquil;


That way, the game knows it can only play for 60f ticks, and it, therefore, initiates the stage. Since currently we're not actually 'turning it on' so to speak. After the game can't find any ActiveStages anymore, it assumes it's done  At least, that is if i'm reading the function well lol

Else you'll need to basically need to do it the somewhat tedious way (Where we use progress as a variable and that variable will get updated by our loop till it reaches 100f., this can be found inside my Jazz tutorial on how to do that instead if my suggestion doesn't work)
Lab Assistant
Original Poster
#87 Old 31st Jan 2021 at 1:52 AM
So like this?
Code:
public override bool Run()
            {
                {
                    TimedStage mBuffTimedStageTranquil = new BuffTimedStage("PracticeYogaUntilTranquil", 60f, false, true, true, BuffNames.Tranquil);
                    base.Stages = new List<Stage>(new Stage[1]
                    {
                    mBuffTimedStageTranquil
                    });
                    base.ActiveStage = mBuffTimedStageTranquil;
                }
Virtual gardener
staff: administrator
#88 Old 31st Jan 2021 at 9:23 AM
Quote: Originally posted by Alunn
So like this?
Code:
public override bool Run()
            {
                {
                    TimedStage mBuffTimedStageTranquil = new BuffTimedStage("PracticeYogaUntilTranquil", 60f, false, true, true, BuffNames.Tranquil);
                    base.Stages = new List<Stage>(new Stage[1]
                    {
                    mBuffTimedStageTranquil
                    });
                    base.ActiveStage = mBuffTimedStageTranquil;
                }
Should do the trick, yep! Now lets hope it does :p
Lab Assistant
Original Poster
#89 Old 6th Feb 2021 at 5:56 AM
Quote: Originally posted by Lyralei
Should do the trick, yep! Now lets hope it does :p

Unfortunately, even with the new line added, the bar still doesn’t fill at all. I may have to try the tedious method you mentioned.
Lab Assistant
Original Poster
#90 Old 7th Feb 2021 at 7:32 PM
So I looked through the TakeBath interaction and found some snippets that could be helpful. First, this looks very similar to what I have now, with the exception of the float line. Maybe that's the key?
Code:
public override void ConfigureInteraction()
	{
		float bathTime = GetBathTime();
		mBathStage = new TimedStage(Localization.LocalizeString("Gameplay/Objects/Plumbing/Bathtub/TakeBath:BatheUntilCleanStage"), bathTime, showCompletionTime: false, selectable: true, visibleProgress: true);
		base.Stages = new List<Stage>(new Stage[1]
		{
			mBathStage
		});
	}

Then here's the code that defines GetBathTime:
Code:
public float GetBathTime()
	{
		Motives motives = Actor.Motives;
		CommodityKind commodityKind = Actor.SimDescription.IsMatureMermaid ? CommodityKind.MermaidDermalHydration : CommodityKind.Hygiene;
		float commodityUpdate = GetCommodityUpdate(commodityKind);
		float commodityUpdate2 = GetCommodityUpdate(CommodityKind.Fun);
		float val = (motives.GetMax(commodityKind) - motives.GetValue(commodityKind)) / commodityUpdate;
		float val2 = 0f;
		float value = motives.GetValue(CommodityKind.Fun);
		if (value < 0f)
		{
			val2 = (0f - value) / commodityUpdate2;
		}
		float num = Math.Max(val, val2);
		float val3 = num * 60f;
		return Math.Max(kMinimumTimeInBath, val3);
	}

I probably don't need any of the commodity stuff, but there may be something here that could fix the problem. I'm just not sure what part yet.
Lab Assistant
Original Poster
#91 Old 8th Mar 2021 at 8:21 PM
I'm finally back. Based on Lyralei's jazz script tutorial I've added some more to the code. Now a completion time shows up when you hover over the action in the que (at 11:30am or at 12:30pm), but the green bar below it still doesn't fill and the sim doesn't exit the action at the time of completion. Also, now the action icon no longer mentions the "Practice Yoga Until Tranquil" string. The moodlet still hasn't shown up either.

Code:
namespace Sims3.Gameplay.Objects.Alunn
{
    public class YogaMat : GameObject
    {
        public static float Progress;
        public static bool IsComplete
        {
            get
            {
                return Progress >= 60f;
            }
        }
        public class PracticeYoga : Interaction<Sim, YogaMat>
        {
            public class Definition : InteractionDefinition<Sim, YogaMat, PracticeYoga>
            {
                public override string GetInteractionName(Sim actor, YogaMat target, InteractionObjectPair interaction)
                {
                    return "Practice Yoga";
                }
                public override bool Test(Sim actor, YogaMat target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    return true;
                }
            }
            public static InteractionDefinition Singleton = new Definition();

            public BuffTimedStage mBuffTimedStageTranquil;
            public override bool Run()
            {
                {
                    TimedStage mBuffTimedStageTranquil = new BuffTimedStage("Practice Yoga Until Tranquil", 60f, true, true, true, BuffNames.Tranquil);
                    base.Stages = new List<Stage>(new Stage[1]
                    {
                    mBuffTimedStageTranquil
                    });
                    base.ActiveStage = mBuffTimedStageTranquil;
                }
                if (!Actor.RouteToSlot(Target, (Slot)0x31229A4C))
                {
                    Actor.PlayRouteFailure();
                    return false;
                }
                // We ask the game to give/initiate the skill on the actor that's currently using the object. Even if they're level 10 or don't know the skill yet, this will be added. 
                Athletic athleticSkill = base.Actor.SkillManager.AddElement(SkillNames.Athletic) as Athletic;
                if (athleticSkill == null)
                {
                    // For good practise on a first run, make sure to add an in-game notification here to debug whether the skill has been loaded or not
                    return false;
                }
                // Starts skill gaining. The Skilling bar will appear above the sim's head now.
                athleticSkill.StartSkillGain(2);
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.Actor.SwitchToOutfitWithSpin(Sim.ClothesChangeReason.GoingToWorkOut, OutfitCategories.Athletic);
                base.SetActor("YogaMat_object", base.Target);
                base.Actor.RouteToSlot(this.Target, (Slot)0x31229A4C);
                base.EnterState("x", "Enter");
                base.SetParameter("skill", (object)Actor.SkillManager.GetSkillLevelParameterForJazzGraph(SkillNames.Athletic));
                base.BeginCommodityUpdates();
                base.AnimateSim("Loop");
                // EA uses the 'Flag' variable for this. But when looping, it's important on each loop to return either 'true' or 'false'. An exit reason can return a false or true 
                bool GainSkillOnLoop = DoLoop(ExitReason.Default, LoopInfinite, null);
                base.AnimateSim("Exit");
                base.EndCommodityUpdates(true);
                // Because we're exiting, we're also stopping skillgaining here.
                athleticSkill.StopSkillGain();
                base.StandardExit();
                // This is the only instance in a run() function to return a variable like this. Merely because of the 'DoLoop' function.
                return GainSkillOnLoop;

            }
            public float DraftProgressTest(InteractionInstance instance)
            {
                return Progress;
            }
            // Our loop function. See how in: bool GainSkillOnLoop = DoLoop(ExitReason.Default, LoopInfinite, null); We call a function in the second parameter? 
            public override bool DoLoop(ExitReason loopExitReasons, InsideLoopFunction loopMe, StateMachineClient smc)
            {
                // I think you can do it without this flag, but this is EA's code so I guess we should give them the benefit of the doubt :p
                bool flag = base.DoLoop(loopExitReasons, loopMe, smc);
                if (flag && base.ActiveStage == mBuffTimedStageTranquil)
                {
                    base.Actor.BuffManager.AddElementPaused(BuffNames.Tranquil, Origin.FromBubbleBath);
                }
                return flag;
            }
            public void LoopInfinite(StateMachineClient smc, LoopData loopData)
            {
                //2 things here, if either we as the player cancel the animation OR the game notices that the sim is super hungry/has no fun/has to go to the toilet, we'll add the exit reasons. 
                if (Actor.HasExitReason(ExitReason.UserCanceled) || (Actor.HasExitReason(ExitReason.MoodFailure)))
                {
                    // AddExitReason so that we 'base.AnimateSim()' will be initiated
                    base.Actor.AddExitReason(ExitReason.UserCanceled);
                    base.Actor.AddExitReason(ExitReason.MoodFailure);
                }
            }
        }
Forum Resident
#92 Old 23rd Mar 2021 at 5:36 PM
Back to replying after not being around for several weeks, just realized there was an update here.

@Lyralei or anyone who could help, have any ideas what could be going on here?

You have been chosen. They will come soon.
Virtual gardener
staff: administrator
#93 Old 23rd Mar 2021 at 7:55 PM
Quote: Originally posted by Jathom95
Back to replying after not being around for several weeks, just realized there was an update here.

@Lyralei or anyone who could help, have any ideas what could be going on here?
You mean as in, whether the mod is coming along?
Forum Resident
#94 Old 23rd Mar 2021 at 10:04 PM
Perhaps I was reading it incorrectly. I haven't been active on MTS much recently due to other things going on, so I haven't been following the thread.

I was reading it as though Alunn had made some more progress but had hot some kind of roadblock or something. If it was just a progress report, that was an error on my part.

You have been chosen. They will come soon.
Lab Assistant
Original Poster
#95 Old 24th Mar 2021 at 10:04 AM
Quote: Originally posted by Jathom95
Perhaps I was reading it incorrectly. I haven't been active on MTS much recently due to other things going on, so I haven't been following the thread.

I was reading it as though Alunn had made some more progress but had hot some kind of roadblock or something. If it was just a progress report, that was an error on my part.

Hey @Jathom95, nice to see you!

My latest posts in this thread have been sorta half progress report, half screaming into the void because VisualStudio hates me. I hit a few (read: several) roadblocks and had to take a step back to figure out what I wanted to accomplish with the mod. I decided to scrap the timed stage stuff because that was giving me the most grief. And as played around with the mat in game, I realized that I'd like for the interaction to just be cancelled either by the user or by motive failure. I'm thinking about allowing sims to get fatigued from practicing yoga, but do people get fatigued after yoga in real life? Idk.

I did figure out how to add a moodlet thanks to Sim State Dude over in the code snippets thread, but I want to delay the tranquil moodlet until after the sim has practiced yoga for at least three sim hours, similar to the pumped moodlet from other work out objects, so it's not too cheaty. I feel like that's probably something that needs to be done via a tuneable comment, but I still don't know how those work yet. Now that I think about it, I also need to figure out how to restrict some of the yoga routines to higher levels of the athletic skill. I'm thinking level 3 for the fun motive routine, level 5 for the social motive routine, and level 7 for the energy motive routine at the moment. Still lots to do, still lots to learn
Lab Assistant
Original Poster
#96 Old 4th Jul 2021 at 12:48 AM Last edited by Alunn : 5th Jul 2021 at 1:40 AM. Reason: Added a spoiler for easier thread navigation
Made some more progress today, though I hit a bit of a snag. I figured out how to gate certain routines at different skill levels by copying what Kit did with their yoga mat code. The energy centering routine requires sims to be at level 7 of the athletic skill, the mind balancing routine requires a minimum of level 5, and the stress relieving routine requires a minimum of level 3. The problem is that in game, only the energy centering routine is greyed out for a sim with no athletic skill. The other two routines are still available, despite the coding being the same as the first. Any idea what's causing this? Here's the code:

Senior Moderator
staff: senior moderator
#97 Old 4th Jul 2021 at 11:56 AM
Glad to see you're still working on it!
I think the interactions are still available because for those two routines, you're still returning true after setting the greyed out tooltip.
On lines 134 and 231, they should be "return false" instead of "return true"

ps you might want to put the code in a spoiler tag when it's pretty long, just so it's easier to navigate the thread :P
Lab Assistant
Original Poster
#98 Old 5th Jul 2021 at 6:58 AM
Quote: Originally posted by zoe22
Glad to see you're still working on it!
I think the interactions are still available because for those two routines, you're still returning true after setting the greyed out tooltip.
On lines 134 and 231, they should be "return false" instead of "return true"

ps you might want to put the code in a spoiler tag when it's pretty long, just so it's easier to navigate the thread :P

Thank you! I can't believe I completely overlooked the return functions Ah well, it's working as intended. I also managed to delay when the tranquil moodlet appears, so now sims will have to practice yoga for 3 in game hours to get the buff. I still need to figure out how to make this tuneable. Right now sims only get the buff from the "Practice Yoga" interaction and not the routines, mostly because sims get a needs boost from doing the routines. I didn't want to make the mat too cheaty, though I'm open to changing this.

The way the sims 3 is coded is finally starting to make sense to me. I've accomplished pretty much everything I set out to do for this mod, with the exception of getting sims to take their shoes off, but that seems more complicated than its worth. Now I just need to test it as much as possible. I guess I could also upload the most recent file for beta testing here as well to see what other simmers think, if that's allowed.
Senior Moderator
staff: senior moderator
#99 Old 5th Jul 2021 at 1:44 PM
That's great! Modding becomes way more fun when it's not so painful just trying to understand anything.
Making that tunable should be pretty easy. You can make a variable with the [Tunable] attribute above it, and make an XML resource with the instance name that's the hash of your namespace.class of where the variable is located. Make sure [Tunable] is added in the assembly info.

Pure scripting involves making a tunable variable (even the variable used at all in the code, it just needs to exist) so looking at the tutorial https://modthesims.info/wiki.php?ti...ripting_Modding should help with adding a tunable value.

Uploading a testing version here is fine! I'm sure people will love it
Forum Resident
#100 Old 5th Jul 2021 at 4:27 PM
Quote: Originally posted by zoe22
Uploading a testing version here is fine! I'm sure people will love it


I know I will.

You have been chosen. They will come soon.
Page 4 of 5
Back to top