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!
Lab Assistant
Original Poster
#1 Old 20th Aug 2020 at 2:09 AM
Yoga Mod Coding - Advice Needed
Hello,

This project is my first attempt at modding in the Sims 3 and I want to make sure I'm on the right track code-wise before I go any further.

I'm trying to create a yoga mat, similar to the one made by Kitabalibar a few years ago, but with a few more animations and animation looping. I also want sims using it to gain the athletic skill, rather than creating an entirely new skill. I don't have much experience with C#, so I've largely been relying on this tutorial by Lyralei. This is what I have so far:

Code:
namespace Sims3.Gameplay.Objects.Alunn
{
    public class YogaMat: RugModernSquare2x1
    {
         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 override bool Run()
            {
       // 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. 
                AthleticSkill athleticSkill = base.Actor.SkillManager.AddElement(SkillNames.Athletic) as AthleticSkill;
                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(SkillNames.Athletic);
 
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.SetActor("YogaMat_object", base.Target);
                base.EnterState("x", "Enter");
                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");
 
       // 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 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);
                }

So far so good?

Also, is it safe to remove the green comments (notes from Lyralei)?
Advertisement
Senior Moderator
staff: senior moderator
#2 Old 20th Aug 2020 at 12:41 PM
I think it looks good so far
Of course, testing in game will be the only way to see if things are working right.
Removing comments should be fine, but you could always keep some/ edit them if you want a reminder on what a particular section of code does.
Virtual gardener
staff: administrator
#3 Old 20th Aug 2020 at 3:52 PM
The comments where indeed just so that, line by line, the person reading the tutorial knew what the code would do at a certain point. So far it's looking good! You seem to be missing 2 brackets at the end, but i'm sure you allready noticed that in visual studio/sharp develop. 

Small note, it doesn't have to be publicclassYogaMat:RugModernSquare2x1. It can also just well be 'GameObject' you're deriving from (rather than RugModernSquare2x1. Either way will work though

If you need some actual jazz scripting help, just feel free to ask it here!  
Lab Assistant
Original Poster
#4 Old 21st Aug 2020 at 1:22 AM
Thank you both for your feedback, glad to know I'm off to an okay start. I switched the RugModernSquare2x1 class back to GameObject as the former was pretty wordy. I'm not sure what happened to the closing bracket that was on line 60, but it is still there in Sharp Develop. Where should the second one go? Also, I thought of a couple more questions and some ideas I'd like your opinions on moving forward.

First, the C# stuff:

1) base.BeginCommodityUpdates(); should I add this in for leveling up the athletic skill between base.EnterState(); and base.AnimateSim();?

2) If I want certain traits to be attracted to (athletic and disciplined) or repelled (couch potato) by the yoga mat, where in the coding would I include this?

3) Much like taking a bubble bath, I'd like sims to have an option to "Practice Yoga Until Tranquil" and receive the moodlet after a period of time has passed. Is this possible? And where could I find it in (what I assume is) the base game coding?

4) How could I flag the yoga mat as a cardio workout so sims don't build insane muscles from using it? (Kitabalibar managed to do this, but I can't access their .dll... unless that's not where the code is located and I'm just noobing right now -_-)

5) If I wanted to add additional interactions, would I place this coding near the bottom before the closing bracket?

Now, the brainstorming stuff:

1) Ideally, I'd like to have four interactions for the yoga mat (practice yoga, basic yoga, intermediate yoga, and advanced yoga), but would practice yoga be a bit redundant given that it plays the same animations as the other three just all together (looping)? There's also the matter of gating off certain animations so they can unlock at higher levels of athletics. That may be above my skill level.

2) Is there a way to have sims switch into their athletic wear and possibly remove their shoes? (IIRC, I think sims can do that with the fire walking pit from IP)

3) To space out the animations, I'm considering putting a few short idle animations between the poses. Yay or nay?

4) Should I add in stumble animations for clumsy and lower level sims or is that a bit complicated?

I hope that's not too much
Virtual gardener
staff: administrator
#5 Old 21st Aug 2020 at 10:51 AM
Quote: Originally posted by Alunn
Thank you both for your feedback, glad to know I'm off to an okay start. I switched the RugModernSquare2x1 class back to GameObject as the former was pretty wordy. I'm not sure what happened to the closing bracket that was on line 60, but it is still there in Sharp Develop. Where should the second one go? Also, I thought of a couple more questions and some ideas I'd like your opinions on moving forward.

First, the C# stuff:

1) base.BeginCommodityUpdates(); should I add this in for leveling up the athletic skill between base.EnterState(); and base.AnimateSim();?

2) If I want certain traits to be attracted to (athletic and disciplined) or repelled (couch potato) by the yoga mat, where in the coding would I include this?

3) Much like taking a bubble bath, I'd like sims to have an option to "Practice Yoga Until Tranquil" and receive the moodlet after a period of time has passed. Is this possible? And where could I find it in (what I assume is) the base game coding?

4) How could I flag the yoga mat as a cardio workout so sims don't build insane muscles from using it? (Kitabalibar managed to do this, but I can't access their .dll... unless that's not where the code is located and I'm just noobing right now -_-)

5) If I wanted to add additional interactions, would I place this coding near the bottom before the closing bracket?

Now, the brainstorming stuff:

1) Ideally, I'd like to have four interactions for the yoga mat (practice yoga, basic yoga, intermediate yoga, and advanced yoga), but would practice yoga be a bit redundant given that it plays the same animations as the other three just all together (looping)? There's also the matter of gating off certain animations so they can unlock at higher levels of athletics. That may be above my skill level.

2) Is there a way to have sims switch into their athletic wear and possibly remove their shoes? (IIRC, I think sims can do that with the fire walking pit from IP)

3) To space out the animations, I'm considering putting a few short idle animations between the poses. Yay or nay?

4) Should I add in stumble animations for clumsy and lower level sims or is that a bit complicated?

I hope that's not too much
1. I actually ended up removing the BeginCommodityUpdates(), since it actually has to do with whether you're giving a Buff to a sim. And a few other factors that I can't remember from the top of my head (might edit this later :p). But skills aren't part of this.
2. You mean autonomously a disciplined sim would use the yoga mat more than a regular sim? Because that's actually done through your ITUN file (yep, more files! :p). However, you can make exceptions where if your sim has a particular trait, it speeds up their skill gaining  If that's what you mean  I'll give you a code example if that's what you're trying to achieve here. And the ITUN stuff can always be explained. I'd advise doing that though once your script is to a point where it's almost done to done. 
3. Actually, yep! Although, it does require some bit of coding. Here's how I'd have tackled it:

Code:
public static float Progress;

public static bool IsComplete
{
   get {
      // You can always change this from 1f to, say, 0.5f
      return Progress >= 1f;
   }
}

public void run()
{
   // Any other code you want here
   // Define how long the interaction should run for. 
   SimpleStage simpleStage = new SimpleStage(GetInteractionName(), GetTimeToCompletion(), DraftProgressTest, true, true, true);
   base.Stages = new List<Stage>(new Stage[1]{   
        simpleStage
   }); 
}

// Needs to be there, actually don't know what this does :p
public float DraftProgressTest(InteractionInstance instance){
   return Progress;
}

public void Loop(StateMachineClient smc, LoopData loopData){
   // Here we add more time to the progress. 
   progress += loopData.mDeltaTime / 300;
   
   // If the IsComplete reached 1f here, then go to this code.   if(IsComplete)
   {
      // Replace with the tranquil one of course.  (see as well: http://www.simlogical.com/ContentUp..._Script_Mod.pdf
      Actor.BuffManager.AddElement(BuffNames.Defeat, Origin.FromBeingSubduedByWasher);
      // Always add this in the loop, this gives it a reason to go out of the loop and finish the loop       base.Actor.AddExitReason(ExitReason.Finished);
   }
}
4. Not quite sure, but I'd have to look at the code a bit more. 
5. ALways place additional interactions after the closing bracket of , like, public class PractiseYoga : Interaction<Sim, YogaMat>. Feel free to check this out: https://github.com/Lyralei1/SewingTable_TS3/blob/master/SewingTable/SewingTable.cs

brain stuff things:
1. I'd actually advise starting just to get it to work  Over time, you can actually specify at a particular skill level when a sim should see a particular interaction. (See test function here: https://github.com/Lyralei1/SewingT...seSewableDIY.cs)  Jazz scripts also alow you to pass through parameters that will basically tell the code that it should run this particular state. But of course this can also be done on a more C# level, with if statements and calling the sim animation that way. Which would basically be something like:

Code:
if(!simSuccess){
   base.AnimateSim("ExitFail");
   base.Actor.AddExitReason(ExitReason.Finished);
} else {
   base.AnimateSim("Exit");
   base.Actor.AddExitReason(ExitReason.Finished);
}
2. Would also have to take a closer look. I know it's possible, but IIRC it requires you to create an additional outfit?
3. I'd say why not! :D
4. If you're super into animating i'd totally go for it! Or you could even have a 'failure' animation through into it occasionally.

Again will have to look at the other 2 questions, but overall everything seems doable
Lab Assistant
Original Poster
#6 Old 22nd Aug 2020 at 7:56 AM
Ah yes, I would like for athletic and disciplined sims to gain the athletic skill faster using the yoga mat.

I think I'm nearly done with the first version of my script. I'm only doing the basic "Practice Yoga" interaction for now to make sure everything works. Next up is putting together the jazz script. A few more questions:

1) I know it's probably late to ask but... how do I make my sims stand on the yoga mat? I was reading over the jazz script tutorial, but the object there is animated while my mat is not. I didn't include the mat in the same blender animation file as the sim rig. Do I need to make my animations a2o or is it okay to leave them as a_?

2) I've sort of roughly planned out how I want the script to flow in terms of sequence as follows:

Step Together (enter)
Intro Pose
(randomized idle animation)
(randomized pose animation)
[transition back to idle]
[transition back to pose]
{when action is cancelled}
Step Apart (exit)

Basically, I need the script to toggle back and forth between randomized idle and pose animations like a switch. Is this possible?

3) Also, when the action is cancelled, I want the sim to have an exit animation so it doesn't look too choppy. Is there a way to do that?

4) For animations, do AFrigs work for both female and male sims? (can you tell this is my first time with animations :P)
Senior Moderator
staff: senior moderator
#7 Old 22nd Aug 2020 at 12:35 PM
I will try to answer your questions 
1) I read about adding routing slots somewhere and I can't find it now! But I did manage to do this before, so I'm pretty sure you'll just need to add a routing slot called RoutingSlot_0 (actually the hash of that, so 0x31229A4C) in the RSLT and then in the script before the animations start, call the Actor.RouteToSlot(Target, Slot.RoutingSlot_0) function.
Now the problem is that rugs don't have RSLTs, so I'm not sure how to add a slot to something without one.
It might be the case you need to use another object with an RSLT as a base instead or maybe someone knows if there's another way. I'm sure I read a thread about it a while ago but also can't find it now.
I think it should be fine to leave the animation names as a_, but I'm not sure what the difference is and when a2o is necessary. But because the yoga mat won't be animated, i think it should be fine?

2) Lyralei might have a better answer on how to implement this, as I don't even know how to get the sim to play idle animations, but it could be a case of using a boolean variable which will just toggle whether or not a yoga animation will play next. 
Like bool playAnimation = true to check which to play, and after each animation/idle animation, you just set it to the opposite: playAnimation = !playAnimation

3) I think you would just need an animation for stepping off the mat which would play in the Exit state perhaps? Or make it so the current animation would just finish before the interaction ends? Over to Lyralei on that one 

4) I am 90% sure the animations for female sims and male sims are completely interchangeable, but it's an easy thing to test 
Lab Assistant
Original Poster
#8 Old 23rd Aug 2020 at 1:48 PM
Looking over the example jazz script in the tutorial again I now see that there is a place for an exit animation (not sure how I missed that in the first place ), so questions #3 and #4 are taken care of. I *think* I've figured out how to implement question #2, but it hinges on whether or not there can be two separate "loops" within the same script. And if there's a limit to how many "weights" I can use when randomizing the looping animations. I'm still not sure what to do about question #1 yet.
Lab Assistant
Original Poster
#9 Old 24th Aug 2020 at 4:58 AM
Sorry for the double post, but I'm stuck on what to do next. The yoga mat shows no interactions in-game. My #C code is unchanged from my first post and this is what my jazz script looks like as of now:

Code:
State Machine "YogaMatAnim"
{
    Properties Default
    Actor "x"
    Actor "YogaMat_object"
    Assign Actor "a_yoga_posture_getIn_x.ma"."x" as "x"
    Assign Actor "a_greetingPose_x.ma"."x" as "x"
    Assign Actor "a_yoga_boatPose_x.ma"."x" as "x"
    Assign Actor "a_yoga_posture_getOut_x.ma"."x" as "x"
    Assign Actor "a_yoga_bridgePose_x.ma"."x" as "x"
    Assign Actor "a_yoga_downwardFacingDog_x.ma"."x" as "x"
    Assign Actor "a_yoga_halfMoonPose_x.ma"."x" as "x"
    Assign Actor "a_yoga_handstand_x.ma"."x" as "x"
    Assign Actor "a_yoga_lordOfTheDance_x.ma"."x" as "x"
    Assign Actor "a_yoga_sidePlankPose_x.ma"."x" as "x"
    Assign Actor "a_yoga_treePose_x.ma"."x" as "x"
    Assign Actor "a_yoga_trianglePose_x.ma"."x" as "x"
    Assign Actor "a_yoga_warrior_x.ma"."x" as "x"
    Assign Actor "a_yoga_idle01_x.ma"."x" as "x"
    Assign Actor "a_yoga_idle02_x.ma"."x" as "x"
    Assign Actor "a_yoga_idle03_x.ma"."x" as "x"
    State "a_yoga_posture_getIn"
    {
        Transitions to "a_greetingPose"
        Play "a_yoga_posture_getIn_x" for "x"
    }
    State "a_greetingPose"
    {
        Transitions to "Loop"
        Play "a_greetingPose_x" for "x"
    }
    // Starting assigning the states with the loops we want
    State "a_yoga_boatPose"
    {
        Transitions to "a_yoga_idle01"
        Play "a_yoga_boatPose_x" for "x"
    }
    State "a_yoga_bridgePose"
    {
        Transitions to "a_yoga_idle02"
        Play "a_yoga_bridgePose_x" for "x"
    }
    State "a_yoga_downwardFacingDog"
    {
        Transitions to "a_yoga_idle03"
        Play "a_yoga_downwardFacingDog_x" for "x"
    }
    State "a_yoga_halfMoonPose"
    {
        Transitions to "a_yoga_idle01"
        Play "a_yoga_halfMoonPose_x" for "x"
    }
    State "a_yoga_handstand"
    {
        Transitions to "a_yoga_idle02"
        Play "a_yoga_handstand_x" for "x"
    }
    State "a_yoga_lordOfTheDancePose"
    {
        Transitions to "a_yoga_idle03"
        Play "a_yoga_lordOfTheDance_x" for "x"
    }
    State "a_yoga_sidePlankPose"
    {
        Transitions to "a_yoga_idle01"
        Play "a_yoga_sidePlankPose_x" for "x"
    }
    State "a_yoga_treePose"
    {
        Transitions to "a_yoga_idle02"
        Play "a_yoga_treePose_x" for "x"
    }
    State "a_yoga_trianglePose"
    {
        Transitions to "a_yoga_idle03"
        Play "a_yoga_trianglePose_x" for "x"
    }
    State "a_yoga_warrior"
    {
        Transitions to "a_yoga_idle01"
        Play "a_yoga_warrior_x" for "x"
    }
    State "a_yoga_idle01"
    {
        Transitions to "Loop"
        Play "a_yoga_idle01_x" for "x"
    }
    State "a_yoga_idle02"
    {
        Transitions to "Loop"
        Play "a_yoga_idle02_x" for "x"
    }
    State "a_yoga_idle03"
    {
        Transitions to "Loop"
        Play "a_yoga_idle03_x" for "x"
    }
    State "a_yoga_posture"
    {
        Transitions to "Exit"
        Play "a_yoga_posture_getOut_x" for "x"
    }
    State "Enter"
    {
        Properties Public, Entry, Explicit
        Transitions to "a_yoga_posture_getIn"
    }
    State "Exit"
    {
        Properties Public, Exit, Explicit
    }
    State "Loop"
    {
        Properties Public, Loop, Explicit
        Transitions to "a_yoga_boatPose"
        Transitions to "a_yoga_bridgePose"
        Transitions to "a_yoga_downwardFacingDog"
        Transitions to "a_yoga_halfMoonPose"
        Transitions to "a_yoga_handstand"
        Transitions to "a_yoga_lordOfTheDancePose"
        Transitions to "a_yoga_sidePlankPose"
        Transitions to "a_yoga_treePose"
        Transitions to "a_yoga_trianglePose"
        Transitions to "a_yoga_warrior"
        Transitions to "a_yoga_posture"
        Select Random
        {
            Weight 1
            {
                Next State "a_yoga_boatPose"
            }
            Weight 1
            {
                Next State "a_yoga_bridgePose"
            }
            Weight 1
            {
                Next State "a_yoga_downwardFacingDog"
            }
            Weight 1
            {
                Next State "a_yoga_halfMoonPose"
            }
            Weight 1
            {
                Next State "a_yoga_handstand"
            }
            Weight 1
            {
                Next State "a_yoga_lordOfTheDancePose"
            }
            Weight 1
            {
                Next State "a_yoga_sidePlankPose"
            }
            Weight 1
            {
                Next State "a_yoga_treePose"
            }
            Weight 1
            {
                Next State "a_yoga_trianglePose"
            }
            Weight 1
            {
                Next State "a_yoga_warrior"
            }
            Weight 1
            {
                Next State "a_yoga_posture"
            }
        }
    }}
Senior Moderator
staff: senior moderator
#10 Old 24th Aug 2020 at 10:03 AM
Hmm I think if the yoga mat isn't showing any interactions at all then it will be a problem with the script rather than the jazz file. 

Did you do the OnStartUp method outside the interaction class but inside the yoga mat's class to do AddInteraction(PracticeYoga. Singleton)? (or whatever the exact code is) 
And is the OBJK definitely referencing your script correctly with your namespace.class?

You could go through the object modding tutorial if you want to make sure you have everything  
Lab Assistant
Original Poster
#11 Old 25th Aug 2020 at 12:13 AM
Quote: Originally posted by zoe22
Hmm I think if the yoga mat isn't showing any interactions at all then it will be a problem with the script rather than the jazz file. 

Did you do the OnStartUp method outside the interaction class but inside the yoga mat's class to do AddInteraction(PracticeYoga. Singleton)? (or whatever the exact code is) 
And is the OBJK definitely referencing your script correctly with your namespace.class?

You could go through the object modding tutorial if you want to make sure you have everything  

I fixed up the code to match the object modding tutorial, but I can't get the script into .dll format with SharpDevelop (the tutorial uses VisualStudio). I also double checked the OBJK to make sure it was referencing my namespace (Sims3.Gameplay.Objects.Alunn.YogaMat) and it does. The new code now looks like this:
Code:
namespace Sims3.Gameplay.Objects.Alunn.YogaMat
{
    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 override bool Run()
            {
       // 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. 
                AthleticSkill athleticSkill = base.Actor.SkillManager.AddElement(SkillNames.Athletic) as AthleticSkill;
                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(SkillNames.Athletic);
 
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.SetActor("YogaMat_object", base.Target);
                base.EnterState("x", "Enter");
                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");
 
       // 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 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);
                }
           public override void OnStartup()
           {
               base.AddInteraction(PracticeYoga.Singleton); 
           }
         }
    }
Senior Moderator
staff: senior moderator
#12 Old 25th Aug 2020 at 5:39 PM
I've never used Sharp Develop, but I imagine it would be a similar thing to VS, if there's an option to Build your project? The dll should probably end up in the project folder somewhere, if it's like VS.
A couple of things to note is the OBJK needs to reference the namespace.class, so if your namespace is Sims3.Gameplay.Objects.Alunn.YogaMat and the class is YogaMat, then it will need to be Sims3.Gameplay.Objects.Alunn.YogaMat.YogaMat.
The other thing is that the OnStartup method needs to be outside of the PracticeYoga interaction class, but still inside the YogaMat class. It seems you have missed off a couple of brackets at the end, I'm not sure if you just didn't copy them, and the OnStartup method is inside the interaction class when it should be outside. If you think about, the OnStartup method is where you add the interactions to the object, so it makes sense that it needs to be outside of any interactions, but within the object's class because that's what it's relating to

So your code should look like this:
Code:
namespace Sims3.Gameplay.Objects.Alunn.YogaMat
{
    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 override bool Run()
            {
                // 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. 
                AthleticSkill athleticSkill = base.Actor.SkillManager.AddElement(SkillNames.Athletic) as AthleticSkill;
                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(SkillNames.Athletic);
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.SetActor("YogaMat_object", base.Target);
                base.EnterState("x", "Enter");
                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");
                // 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 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);
                }
            }
        }
        public override void OnStartup()
        {
            base.AddInteraction(PracticeYoga.Singleton);
        }
    }
}
Lab Assistant
Original Poster
#13 Old 25th Aug 2020 at 10:14 PM
Quote: Originally posted by zoe22
I've never used Sharp Develop, but I imagine it would be a similar thing to VS, if there's an option to Build your project? The dll should probably end up in the project folder somewhere, if it's like VS.
A couple of things to note is the OBJK needs to reference the namespace.class, so if your namespace is Sims3.Gameplay.Objects.Alunn.YogaMat and the class is YogaMat, then it will need to be Sims3.Gameplay.Objects.Alunn.YogaMat.YogaMat.
The other thing is that the OnStartup method needs to be outside of the PracticeYoga interaction class, but still inside the YogaMat class. It seems you have missed off a couple of brackets at the end, I'm not sure if you just didn't copy them, and the OnStartup method is inside the interaction class when it should be outside. If you think about, the OnStartup method is where you add the interactions to the object, so it makes sense that it needs to be outside of any interactions, but within the object's class because that's what it's relating to

Thank you for the clarification, that'll teach me to try to code late at night haha. The code has been fixed (also yes for some reason the last few brackets didn't copy over, they still show in SharpDevelop). But I'm still having problems trying to get the code into .dll format. Apparently, in SharpDevelop, I can't compile individual files and have to create a project, but the option to create a project is grayed out. Then I tried creating a solution, but I keep getting this error message: "The type or namespace name 'Sim' could not be found (are you missing a using directive or an assembly reference?) (CS0246)", along with 11 other errors with the same tag. I briefly wrestled with Visual Studio, but in order to follow the object modding tutorial, I'd have to pay to get Reflector
Senior Moderator
staff: senior moderator
#14 Old 25th Aug 2020 at 11:17 PM
Hmm that's weird, I'm not sure why that would be that you can't compile. I'm sure @Battery could help
FYI, you don't  need to use Reflector to use VS, as they are for completely separate things - I use VS and ILSpy but I am used to VS, whereas SharpDevelop is meant to be easier for beginners as I think it's easier to set up for sims 3?

Actually, if it's giving errors like that, it might be that you need to add the references at the top (at least thats how it works in VS, in assuming it's the same for SD) so for example:
using Sims3.Gameplay.Actors etc. 
With VS you can click on the error and there's an option to automatically add the reference, so perhaps there's something similar with SD. But someone who actually knows how to use SD will actually be able to help :P
Virtual gardener
staff: administrator
#15 Old 26th Aug 2020 at 3:00 PM
It does indeed sound like you're missing adding at the start before the namespace to add the 'using Sims3.Gameplay.Actors'. 

With SharpDevelop (and I think Visual studio as well) you can actually click on the 'Sim' bit that's giving an error, and you should then see on the left side a pencil icon. Click that, and then there should be a 'add using Sims3.Gameplay.Actors'. With Visual studio that's right-clicking onto that Type and I seem to remember that that's about the same selection. 

SharpDevelop is indeed a bit easier because Visual Studio 2008 (which was the last version that had that option for 'Do not Reference mscorlib.dll'. And VS 2008 is kinda hard to track down these days :p There's a few other advantages of course (not needing a Microsoft account, etc). 
Lab Assistant
Original Poster
#16 Old 27th Aug 2020 at 4:57 AM
Quote: Originally posted by Lyralei
It does indeed sound like you're missing adding at the start before the namespace to add the 'using Sims3.Gameplay.Actors'. 

With SharpDevelop (and I think Visual studio as well) you can actually click on the 'Sim' bit that's giving an error, and you should then see on the left side a pencil icon. Click that, and then there should be a 'add using Sims3.Gameplay.Actors'. With Visual studio that's right-clicking onto that Type and I seem to remember that that's about the same selection. 

SharpDevelop is indeed a bit easier because Visual Studio 2008 (which was the last version that had that option for 'Do not Reference mscorlib.dll'. And VS 2008 is kinda hard to track down these days :p There's a few other advantages of course (not needing a Microsoft account, etc). 

That seemed to be the missing piece! I managed to get it into the game fairly smoothly. The sims still don't stand on the mat, so I need to figure out a workaround for that, and something is up with the jazz script. It completely skips over the "a_greetingPose_x" animation and randomly stops cycling through animations. But the action doesn't cancel out, so the sim just stands there (maybe it's some kind of delay?). It also seems to cycle through the same 3 or 4 animations instead of the full list. Any ideas on why that is?
Senior Moderator
staff: senior moderator
#17 Old 27th Aug 2020 at 1:21 PM Last edited by zoe22 : 27th Aug 2020 at 3:48 PM.
For the routing, if you don't want to add a routing slot somehow you could use this bit of code, that I used for my well mod that I got from the put away tent interaction. Instead of having a slot to route the sim to, the sim just goes to a point within a certain radius of the object (at least that's what i think :p, I'm not sure of exactly what each line does, but you should be able to change the numbers to make it so the sim will route to somewhere near the middle of the rug).
Here, which you can add at the start of your Run() code: 
Code:
Route route = Actor.CreateRoute();
                route.SetOption(Route.RouteOption.DoLineOfSightCheckUserOverride, value: true);
                float num = 1f;
                float num2 = 2f;
                IHasRouteRadius hasRouteRadius = Target as IHasRouteRadius;
                if (hasRouteRadius != null)
                {
                    float num3 = 3f;
                    num = hasRouteRadius.CarryRouteToObjectRadius;
                    num2 = num * num3;
                    if (num2 - num > 2f)
                    {
                        num2 = num + 2f;
                    }
                }
                route.PlanToPointRadialRange(Target.Position, num, num2, RouteDistancePreference.PreferNearestToRouteDestination, RouteOrientationPreference.TowardsObject, Target.LotCurrent.LotId, new int[1]
                {
                Target.RoomId
                });
                Vector3 position = Target.Position;
                if (!Actor.DoRoute(route))
                {
                    return false;
                }
                if (position != Target.Position)
                {
                    return false;
                }

edit: I thought i added this before but I guess I somehow deleted it? weird but anyway, the problem might be that the sim won't necessarily end up facing the right direction to do the animations, but I think the only other alternative would be to use an item with an RSLT resource and add the routing slot to that.
Lab Assistant
Original Poster
#18 Old 22nd Sep 2020 at 7:38 AM
Finally had time to recreate my yoga mat as a deco object to give it an RSLT, but I'm unsure of how to add the routing slot to the RSLT in S3PE (and what to do with the hash number 0x31229A4C). Also, I don't know where in the script to call the Actor.RouteToSlot(Target, Slot.RoutingSlot_0). I'm assuming I put it in the wrong place because the package file crashed my game to desktop immediately.

ETA: I also added the parameter of the sim needing to change into their athletic wear (based on Simoro's code) and specified the animation in my .jazz file, but the sims don't change clothes, though they do spin like they're supposed to. Maybe I'm missing something on the script side?
Senior Moderator
staff: senior moderator
#19 Old 24th Sep 2020 at 10:36 AM
For the routing slot, click on the RSLT in the package and then click grid at the bottom of the S3PE screen. Open up ChunkEntries, [0]ChunkEntries, RCOL Block, and find Routes. You can click on the 3 dots on the right and you should be able to add a routing slot from there. The slot name will be 0x31229A4C, and I think the bone name should be the transform bone, so the hash of that is 0xCD68F001. Then test in game, hopefully it will work fine, though maybe you will need to edit the rotation of the slot. Honestly not sure which values to change for that in s3pe, when I did it I used cmomoney's RSLT slot editor tool for Blender, but I find those tools pretty temperamental and they won't work for some reason importing/exporting. You might be able to edit it in TSRW easier, but I think that involves saving a new package and you have to reimport the script and jazz stuff? Anyway there is no point in worrying about that until you need to :P

And so at the beginning of your Run method, you put something like this:
Code:
 if (!Actor.RouteToSlot(Target, Slot.RoutingSlot_0))
                {
                    Actor.PlayRouteFailure();
                    return false;
                }

Not sure about the outfits though, I've never done that before
Lab Assistant
Original Poster
#20 Old 27th Sep 2020 at 9:58 AM
I added the routing slot in the RSLT and put the routing code in the run method, but my game still crashes to desktop even after clearing the cache files multiple times. This is what the new code snippet looks like:
Code:
            public override bool Run()
            { if (!Actor.RouteToSlot(Target, Slot.RoutingSlot_0))
                {
                    Actor.PlayRouteFailure();
                    return false;
                }

Am I missing something?
Senior Moderator
staff: senior moderator
#21 Old 27th Sep 2020 at 5:28 PM
Hmm I'm not exactly sure why it would crash, whether it's the code or the package. You could test it by commenting out the new code and see if that stops it crashing, and if not try removing the slot from the package in case that is the problem. If removing both doesn't help, perhaps something else has gone wrong with the package, and it might be worth trying an older version if you have it, or reassemble the package with just the basics and go from there.
Lab Assistant
Original Poster
#22 Old 30th Sep 2020 at 3:32 AM
I definitely has something to do with the code, but I have no idea what. This is what I have as of now (I know some of the usings are unnecessary):
Code:
using Sims3.SimIFace;
using System;
using System.Text;
using Sims3.Gameplay.Objects.Miscellaneous;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.EventSystem;
using Sims3.UI;
using Sims3.Gameplay.Abstracts;
using Sims3.Gameplay.DreamsAndPromises;
using Sims3.Gameplay.ActorSystems;
using Sims3.Gameplay.Utilities;
using Sims3.Gameplay.TuningValues;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.Interactions;
using Sims3.Gameplay.Skills;
using Sims3.Gameplay.Core;
using Sims3.SimIFace.SACS;
using Sims3.Gameplay.Roles;
using System.Collections.Generic;
using Sims3.Gameplay.Interfaces;
using Sims3.Gameplay.Objects.Decorations.Mimics;

namespace Sims3.Gameplay.Objects.Alunn.YogaMat
{
    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 override bool Run()
            { if (!Actor.RouteToSlot(Target, Slot.RoutingSlot_0))
                {
                    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((float)SkillNames.Athletic);
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.SetActor("YogaMat_object", base.Target);
                base.EnterState("x", "Enter");
                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");
                // 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 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);
                }
            }
        }
        public override void OnStartup()
        {
            base.AddInteraction(PracticeYoga.Singleton);
        }
    }
}
Virtual gardener
staff: administrator
#23 Old 30th Sep 2020 at 2:39 PM Last edited by Lyralei : 30th Sep 2020 at 2:43 PM. Reason: Fixing weird code notation
From the code you just parsed, I do have my suspicions on this line:

Code:
athleticSkill.StartSkillGain((float)SkillNames.Athletic)

And the reason for me thinking that is really because in your previous piece of code you didn't cast it as a float. So it was like this:

Code:
athleticSkill.StartSkillGain(SkillNames.Athletic)
Now the reason you can't cast it as a float, is because it's a ulong type. And it's pretty much impossible to convert an enum entry to a float number (Floats being any numbers that we'd consider being able to have a decimal behind it. So: 1.25, 2.80, etc.) 

What I did with my sewing table was this:

Code:
kSewingSkillGainRate = 30f;
sewingSkill.StartSkillGain(kSewingSkillGainRate);
And voila!  

Now if that does work, I'd suggest putting print statements on every single line at the run function. usually how I do this is:

Code:
public override bool Run()
            { if (!Actor.RouteToSlot(Target, Slot.RoutingSlot_0))
                {
                     print("Actor could not route");
                    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;
                  print("Element seemingly found");
                if (athleticSkill == null)
                {
                  print("Skill returned 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;
                }
                  print("Going to do skill gain");
                // Starts skill gaining. The Skilling bar will appear above the sim's head now.
                athleticSkill.StartSkillGain((float)SkillNames.Athletic);
                  print("Started skill gain");
                base.StandardEntry();
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.SetActor("YogaMat_object", base.Target);
                base.EnterState("x", "Enter");
                base.AnimateSim("Loop");
               print("Did all the animation stuff");
                // 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 
               print("Going to enter Do loop");
                bool GainSkillOnLoop = DoLoop(ExitReason.Default, LoopInfinite, null);
               print("Finished loop");
                base.AnimateSim("Exit");
                // Because we're exiting, we're also stopping skillgaining here.
               print("Going to stop skill gaining");
                athleticSkill.StopSkillGain();
               print("Stopped skill gaining");
                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 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)))
                {
                  print("Has exit reason");
                    // AddExitReason so that we 'base.AnimateSim()' will be initiated
                    base.Actor.AddExitReason(ExitReason.UserCanceled);
                    base.Actor.AddExitReason(ExitReason.MoodFailure);
                }
            }
        }
      public static void print(string text)
      {
         StyledNotification.Show(new StyledNotification.Format(text, StyledNotification.NotificationStyle.kDebugAlert));
      }

because when something crashes something, debugging the everliving hell out of your code is always the best thing to do. In the case of the sims, that's either with Nraas Error trap or print statements (I'd advice both tbh :p). It's really the most practised way of debugging for most programmers and any code in existence
Lab Assistant
Original Poster
#24 Old 1st Oct 2020 at 12:02 PM
VisualStudio suggested adding (float) to the line to resolve an error message the prevented me from building the solution. I changed the line to look like this, based on the information from Sims3.Gameplay.Skills:
Code:
print("Going to do skill gain");
                // Starts skill gaining. The Skilling bar will appear above the sim's head now.
                athleticSkill.StartSkillGain(2);
                print("Started skill gain");

Still experiencing crashing. I also added the print statements to the run method, but the game crashes before it can even generate a script cache, so I don't know if that works.
Lab Assistant
Original Poster
#25 Old 6th Oct 2020 at 12:58 PM Last edited by Alunn : 6th Oct 2020 at 1:14 PM.
Sorry for the double post, but I stumbled upon this thread about routing slots and decided format my code in a similar fashion. It still crashes, unfortunately, but hopefully I'm getting somewhere. This is the most recent version of the run method:
Code:
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; 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.Actor.RouteToSlot(Target, (Slot)0x31229A4C);
                base.EnterStateMachine("YogaMatAnim", "Enter", "x");
                base.SetActor("YogaMat_object", base.Target);
                base.EnterState("x", "Enter");
                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");

                // 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 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);
                }
            }
        }
        public override void OnStartup()
        {
            base.AddInteraction(PracticeYoga.Singleton);
        }
    }
}

Also, now I'm getting two new errors in VS:

1."The call is ambiguous between the following methods or properties: 'YogaMat.PracticeYoga.LoopInfinite(StateMachineClient, InteractionInstance.LoopData)' and 'YogaMat.PracticeYoga.LoopInfinite(StateMachineClient, InteractionInstance.LoopData)"

2."Ambiguity between 'YogaMat.PracticeYoga.Singleton' and 'YogaMat.PracticeYoga.Singleton'"
Page 1 of 5
Back to top