Quick Reply
Search this Thread
Virtual gardener
staff: administrator
Original Poster
#1 Old 7th Aug 2020 at 3:57 PM Last edited by Lyralei : 25th Mar 2021 at 1:17 PM. Reason: Changed the title to something more understanding
Default [Technical thread] TS2 > TS3 Sewing table BETA
Heya everyone!

I had been thinking of doing this for a while and kindly stealing Arsil's habit of making these threads :p. The sewing table is a BIG project and as one might know, full time jobs and adulthood does minimize the time I can work on it. Therefore, I decided to put it up on Github: https://github.com/Lyralei1/SewingTable_TS3

Does this mean I can contribute to the code?
Heck yes, you can! I put some info about this in the 'Readme' bit.  However, it does mean you need some Github knowledge. If you're not familiar with it, see: https://www.youtube.com/watch?v=nhNq2kIvi9s And (if you want to use sourcetree): https://www.youtube.com/watch?v=1lBdlh3AGSc

Obviously, you do need to ask to become a contributor. This is also so that not everyone can commit their code, just to protect it from trolls or untrusty peeps. Feel free to PM me your Github email address and I'll add you right to the contributors page!

Any issues that are in the repository page are issues that have been decided on to be fixed/added. Meaning you can work on those issues. Any ideas should probably be discussed here. Unless you guys prefer github for that

What about this thread
Any users with issues regarding the sewing table can ask stuff here. Or any feature requests, we can also discuss this here. Basically, this thread serves for helping each other, discussing any features, and generally the sewing table

What else?
I do want to be upfront that the code for the table is... all over the place :p I still want to go in and edit/merge some code together. A lot has changed from the point I started and it also still has some stuff that was used back then but sort of works now. Most of that is removed, but I'll do some code optimisation.
Advertisement
Virtual gardener
staff: administrator
Original Poster
#2 Old 11th Aug 2020 at 1:06 PM
Heya! 

I'm currently working on a way to expand the sewing skill along with some bug fixes of course (Since programming feels like you're taping holes on a balloon and then a next hole starts to exist :p) I was wondering if you guys had any feedback, features, things, ideas, etc. Here's what I currently have:

GOOD O' STATS:
- Counting how many projects have been done/finished.
- Patterns discovered
- Counting how many 'hardest' projects they have made.


CHALLENGES:
- 'It doesn't take a village': Has created over 400(?) projects. 
- 'To Perfection!': Has done sewing for 100(?) hours total.

OTHER STUFF:
Adding all these interactions (see: https://sims.fandom.com/wiki/Arts_and_Crafts ) :
  • "Talk about Arts and Crafts"
  • "Read Section ... Arts" in newspaper
  • "Browse Web About Arts and Crafts" on the computer
  • "Share Hobby Tips" with a Sim
  • "Blog About Arts and Crafts" on the computer
  • "Instruct in Arts and Crafts"
  • "Be in the Zone" (might not do it but dunno)
- The plagues that come with it. Which we can add to the skill journal possibly as an illustration and mailbox. 

Maybe's: 
- Mindfulness can be achieved in real life by accepting that something didn't work out (like a project that doesn't work) and I think it can get expanded into something bigger. However, injecting that into other skills and anything that should make said sim angry, can be tough. We also have hot-headed as a trait, which makes it feel even more unnecessary to add to the game. 
- Training creativity. Which can also be trained in real life. Heck, there are even hundreds of courses and trainings, out there who help out in that. We've got the painting skill and sculpting skill that is considered a creative skill in the game. However, the question is, how much would such a thing impact the game?

Would love to hear more!
Virtual gardener
staff: administrator
Original Poster
#3 Old 11th Aug 2020 at 11:13 PM
Heya @gamefreak130 ! 

So I just applied this fix that you mentioned in the comments (See: https://modthesims.info/download.ph...153#post5663153

Now, the first time it runs, it's great! The second time, however, it does freeze the game. It's a start from it always freezing though :p

Basically how it's structured currently is:

Code:
   
//Interaction class stuff, etc etc
         
public override bool Run()
{    
   // Additional code that's unnecessary currently
   bool GainSkillOnLoop = DoLoop(ExitReason.Default, LoopInfinite, null);
   // More additional code + returning the Loop boolean. 
}

public void LoopInfinite(StateMachineClient smc, LoopData loopData)
{
    Sims3.SimIFace.Simulator.AddObject(new OneShotFunctionTask(new Function(AddingPatterns)));
}

public void AddingPatterns() { // Again, stuff that handles the patterns }
But alas. You were saying about adding a while loop, although I'm logistically thinking how to implement that by not having 50 objects show up in 1 second or so if it were added in the LoopInfinite function :p
Instructor
#4 Old 12th Aug 2020 at 12:19 AM
Quote: Originally posted by Lyralei
Heya @gamefreak130 ! 

So I just applied this fix that you mentioned in the comments (See: https://modthesims.info/download.ph...153#post5663153

Now, the first time it runs, it's great! The second time, however, it does freeze the game. It's a start from it always freezing though :p


You mean the interaction will run once without issue but freeze on subsequent attempts? And you've replicated this behavior multiple times, as a sanity check? That's very odd. Assuming you're using the default DoLoop implementation, I don't see anything that would cause a great deal of problems. Just for debugging purposes, does the freezing still occur if you remove all behavior related to adding patterns?

Quote: Originally posted by Lyralei
I'm logistically thinking how to implement that by not having 50 objects show up in 1 second or so if it were added in the LoopInfinite function :p


I assume you have some conditionals in AddingPattern that check whether or not a pattern needs to be added and/or which one it is? Why not just extract those out to the loop, so that AddingPatterns just executes whatever resource-intensive activities are needed to, well, add the pattern. That way, you'll only put work on the Simulator when you need to.

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Virtual gardener
staff: administrator
Original Poster
#5 Old 12th Aug 2020 at 3:55 PM
Quote: Originally posted by gamefreak130
You mean the interaction will run once without issue but freeze on subsequent attempts? And you've replicated this behavior multiple times, as a sanity check? That's very odd. Assuming you're using the default DoLoop implementation, I don't see anything that would cause a great deal of problems. Just for debugging purposes, does the freezing still occur if you remove all behavior related to adding patterns?
Yep and yep! You can indeed call it a sanity check indeed :p But I'm possibly missing something again like that sewing skill book thing we looked at before. The DoLoop function is indeed EA's code. I will come back to you when I have tested the freezing happening on the more accessive bits. 

Quote: Originally posted by gamefreak130
I assume you have some conditionals in AddingPattern that check whether or not a pattern needs to be added and/or which one it is? Why not just extract those out to the loop, so that AddingPatterns just executes whatever resource-intensive activities are needed to, well, add the pattern. That way, you'll only put work on the Simulator when you need to.
Multiple actually, plus if it returns something of course. AddingPatterns() currently basically gets a returned value (an GameObject) from DiscoverPatternForGlobalObjects() (Which I really need to change to the sewing version of that code). Since it's pretty big, you should be able to see it here: https://github.com/Lyralei1/SewingTable_TS3/blob/master/GlobalsSewingTable/Pattern.cs

Currently, the AddingPatterns() does have a check that sees if DiscoverPatternsForGlobalObjects() actually returned an GameObject (so whether it's not null). it also has the whole checking the chance of getting to the code to begin with, which I might as well move around the OneShot functionality, rather than AddingPatterns.

As you can see, DiscoverPatternsForGlobalObjects needs to store a lot of data. compare that data with any custom settings and the third party imported objects, as well as set it all up. So it's a lot to take in. It's indeed a good idea in that case to just, before practising, prepare a few random patterns beforehand and just give them to the sim occasionally on the loop. That way it's not as accessive as you said.  So I'll try that too!
Virtual gardener
staff: administrator
Original Poster
#6 Old 15th Aug 2020 at 10:54 AM
Hey @gamefreak130 !

I've tried this method:

Quote:
I assume you have some conditionals in AddingPattern that check whether or not a pattern needs to be added and/or which one it is? Why not just extract those out to the loop, so that AddingPatterns just executes whatever resource-intensive activities are needed to, well, add the pattern. That way, you'll only put work on the Simulator when you need to.
In 4 different ways actually. One was more time based, the other would collect it and once it felt like it, share it with said sim. And another method which would initially engage outside the loop. Which would freeze on the game as well. So I'm even suspecting that the script itself is alright on load time, also tried asynchronizing the code, and at the end of executing another functionality, but when it has to do it more times, the game hates it. Sometimes makes me wish the game was 64-bit, but alas. 

So that would probably mean I have to rewrite that bit of code, or implement some sort of caching functionality. (Which I think you were referring to?). So that will be fun :p

Thanks for your help though! I think the initial suggestion of using oneshotfunction was a good one, but unfortunately, that's not doing it with my setup, unless I make the script less resource-intensitive. 
Virtual gardener
staff: administrator
Original Poster
#7 Old 15th Aug 2020 at 1:33 PM
Upon more research, I did notice that the Chemistrytable has one more overload more than mine in it's "DoLoop" function, which is where we ask the DoLoop to basically notify us based on simMinutes. The way how they do it, is that they do the discovery code right inside the loop, which was initially why I went with adding it to the loop function. 

So let's see if I can somehow take that to my advantage! :p
Virtual gardener
staff: administrator
Original Poster
#8 Old 15th Aug 2020 at 2:48 PM Last edited by Lyralei : 15th Aug 2020 at 3:35 PM.
There we go! So I decided to not rewrite the whole discovery method, but I did notice I had a LOT of for loops. And, as great as they are, they can be quite resource-intesive when they need to run, say, 30 times. So I decided to cache the resourceKeys that we get upon worldload into a seperate list. And that seems to do it flawlessly!

It does mean the patterns don't show up directly, but do show up once you stop the interaction. But this is the best I can do for now.

EDIT: Would someone be able to test this version of the sewing table? It's working-ish for me, with some rare freezing when I run the interaction on the quickest setting. 
Attached files:
File Type: zip  LYRALEI_GREENPLUMBBOBLOVER_SewingTable_BETA_V1.0.3.zip (7.25 MB, 9 downloads)
Virtual gardener
staff: administrator
Original Poster
#9 Old 29th Aug 2020 at 3:57 PM
Coming back to this issue where on discovery on a loop it would freeze. Having debugged the everliving hell out of it, I found out it seems to get stuck at a particular point when the 'DiscoverPattern' function is called for a second time by the game. In fact, it's, somehow stuck in an infinite loop here.

This is the loop:
Code:
             do 
            {
                getPattern = GetRandomKeyWithSKills(actor);
                GlobalOptionsSewingTable.print("mDiscovered count return:" + GlobalOptionsSewingTable.retrieveData.mDiscoveredObjects.Count.ToString());
                if(!GlobalOptionsSewingTable.retrieveData.mDiscoveredObjects.ContainsValue(getPattern) && !GlobalOptionsSewingTable.retrieveData.mDiscoveredObjects.ContainsKey(actorDesc))
                {
                    GlobalOptionsSewingTable.print("Found resourcekey to return:" + getPattern.ToString());
                    // break the loop if a not-known pattern has been found. 
                    break;
                }
                GlobalOptionsSewingTable.print("pattern hasn't been discovered yet");
            }
            while(true);

GetRandomKeyWithSkills() will basically do a 'RandomUtils.GetRandomObjectFromList()' depending on the skillLevel of the sim. This is important, because we want to get the random object from either the easy list, hardsewable list or Hardestsewable list. Each list contains resource keys from in-game and CC content of what's considered to us as 'easy' patterns or 'hard' patterns. 

This all works as expected. On the first run it will return a pattern, breaks out of the do loop and basically continues executing the other code + the loop.

Then, we get to a point where it calls in the loop function inside the run() option the function again. Now, this 'do' loop seems to be stuck in an infinite loop. It returns keys that the sim doesn't know, the mDiscoveredObject returns a count of 1 now (as it should) and such, but somehow the loop isn't picking up on the fact that the if condition is now supposed to be true and break out of the loop. 

Does anyone have any idea why this is happening? Never came across such a weird bug tbh.
Senior Moderator
staff: senior moderator
#10 Old 29th Aug 2020 at 5:14 PM Last edited by zoe22 : 29th Aug 2020 at 6:25 PM.
Is it because once the sim has found a pattern then GlobalOptionsSewingTable.retrieveData.mDiscoveredObjects.ContainsKey(actorDesc) will be true, so NOT that will be false, and so the if statement will be false?
So it works the first time, but after the sim has found one pattern, would the actorDesc be added to the mDiscoveredObjects?
I'm not exactly sure how all your code works though of course :p
Virtual gardener
staff: administrator
Original Poster
#11 Old 29th Aug 2020 at 7:59 PM
Quote: Originally posted by zoe22
Is it because once the sim has found a pattern then GlobalOptionsSewingTable.retrieveData.mDiscoveredObjects.ContainsKey(actorDesc) will be true, so NOT that will be false, and so the if statement will be false?
So it works the first time, but after the sim has found one pattern, would the actorDesc be added to the mDiscoveredObjects?
I'm not exactly sure how all your code works though of course :p
Well actorDesc is actually the simDescription, which gives back the simID (or some ID). mDiscoveredObject is a dictionary, which holds a ResourceKey and SimDescription. So how I thought the If statement would be is more or less: If both the Resourcekey (KeyValue) and the SimDescription (Value) can't be found, then use this resourceKey to make a pattern out of it.

Although I will say I am trying to re-wrap the logic in my head now lol. Because we do want to make sure that each sim that discovers something have their own little list of resource keys that shows what they already know and what they don't
Virtual gardener
staff: administrator
Original Poster
#12 Old 1st Sep 2020 at 10:44 AM Last edited by Lyralei : 2nd Sep 2020 at 9:33 PM.
So, @zoe22 pointed out to me the other day that the condition is obviously what is going wrong here :p because, not only am I checking whether the resourcekey already exists in that particular list, i'm also checking if the Sim has been discovered. meaning, that it will always return a true, because on the very first 'discovery' made, we add the sim to the list as well. 
So I'm now adjusting that bit and the dictionary and let's hope that fixes it!

EDIT: Allright! That fixed it! Plus the freezing issue. Which wasn't necessarily where the game got too much pressure going on, it was really that it was stuck in an infinite loop. IIRC the Simulator even has a timeout for these types of things, so I guess thats why the game froze? It's fixed now though
Virtual gardener
staff: administrator
Original Poster
#13 Old 5th Sep 2020 at 1:56 PM
Currently I'm working on some code that defines whether the sim is now a 'Sewing master". Now I'm going around with this in my head and would love some feedback.

Currently, I got it that you need to have discovered all patterns to be a sewing master. But I also feel like we need to do a bit more? Especially since it doesn't make much sense, given how one could literally only practise sewing and get all the patterns without having made a project before.

I'm thinking, they need to be level 10, know all patterns and having done at least a 100 projects successfully?
Senior Moderator
staff: senior moderator
#14 Old 5th Sep 2020 at 11:36 PM
I think that would make sense, because just knowing patterns wouldn't mean you can actually sew them :P
Are these like skill challenges? Maybe you could do something like "Pattern collector" or "Knowledgeable Sewer" for sims who collect a lot (or all) patterns and then they get a perk?
And another one for sims who have made 100 projects successfully, so then when they get level 10 and complete both challenges they become the Ultimate Sewing Master!!
I dunno though, these are just some ideas :P
Virtual gardener
staff: administrator
Original Poster
#15 Old 6th Sep 2020 at 3:41 PM
Quote: Originally posted by zoe22
I think that would make sense, because just knowing patterns wouldn't mean you can actually sew them :P
Are these like skill challenges? Maybe you could do something like "Pattern collector" or "Knowledgeable Sewer" for sims who collect a lot (or all) patterns and then they get a perk?
And another one for sims who have made 100 projects successfully, so then when they get level 10 and complete both challenges they become the Ultimate Sewing Master!!
I dunno though, these are just some ideas :P
Hey! I like that  The perks I guess (like where, with handiness, you get a discount for all buy mode items). So maybe someone can find patterns quicker when they got the pattern collector or the Ultimate sewing master has a special combat move  gets discounts on fabrics! I did hear that people thought 200 was expensive. But fabrics are expensive in real life too
Virtual gardener
staff: administrator
Original Poster
#16 Old 14th Sep 2020 at 11:56 AM
Almost all interactions seem to be working and ready to go. However, I've one issue with 'Blog about Sewing" on any sim's phones. The debugging I put in, claims that the interactions have been parsed, but in reality, it hasn't. 

On worldload I do the following:

Code:
                foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
                {
                    if (sim != null)
                    {
                        if(sim.Inventory.Find<Phone>() != null)
                        {
                            print("Found phone");
                            Phone phone = sim.Inventory.Find<Phone>();
                            AddInteractionsPhone(phone);
                        }
                        AddInteractionsSims(sim);
                    }
                }

This is the phone interactions function:
Code:
        private static void AddInteractionsPhone(Phone phone)
        {
            foreach (InteractionObjectPair interaction in phone.Interactions)
            {
                if (interaction.InteractionDefinition.GetType() == GottaBlogAboutSewing.Singleton.GetType())
                {
                    return;
                }
            }
            print("adding interaction");
            phone.AddInteraction(GottaBlogAboutSewing.Singleton);
            print("added interaction");
        }
And the OnSimInstantiated listener:

Code:
        private static ListenerAction OnSimInstantiated(Event e)
        {
            try
            {
                Sim sim = e.TargetObject as Sim;
                if (sim != null)
                {
                    AddInteractionsSims(sim);
                }
                Phone phone = e.TargetObject as Phone;
                if (phone != null)
                {
                    AddInteractionsPhone(phone);
                }
            }
            catch (Exception ex2)
            {
                print("ListenerAction exc: /n /n" + ex2.ToString());
            }
            return ListenerAction.Keep;
        }
GottaBlogAboutSewing the function itself is pretty much the same as the 'Phone.BrowseTheWeb' function (See the class in ILspy

I've tried directly parsing it through PhoneSmart as well (so in the Find function we look for PhoneSmart, rather than Phone) But that didn't do it either.

Any ideas? It seems ITUN isn't super necessary for it to work, unlike some things EA implemented
Virtual gardener
staff: administrator
Original Poster
#17 Old 14th Sep 2020 at 1:53 PM
Turns out, it's 'technically' assigned to the phone. It's just the physical object (phone) however :p So when a sim has their phone out, only then does it show the option to blog about sewing...

So I guess I should look more into the UI code rather than the game object itself
Instructor
#18 Old 14th Sep 2020 at 4:55 PM
Quote: Originally posted by Lyralei
Turns out, it's 'technically' assigned to the phone. It's just the physical object (phone) however :p So when a sim has their phone out, only then does it show the option to blog about sewing...

So I guess I should look more into the UI code rather than the game object itself


All game objects have a separate list of InteractionObjectPairs used when they are inside of a Sim's inventory. When adding interactions to cell phones, laptops, etc. you have to explicitly add the interaction as an inventory interaction if you want it to be available from there.

As far as I know, there's no harm in adding inventory interactions to an object that can't be put into the inventory, so I use a generic method that adds the interaction to both lists:

Code:
public static void AddInteraction(GameObject gameObject, InteractionDefinition singleton)
        {
            foreach (InteractionObjectPair iop in gameObject.Interactions)
            {
                if (iop.InteractionDefinition.GetType() == singleton.GetType())
                {
                    return;
                }
            }
            if (gameObject.ItemComp?.InteractionsInventory != null)
            {
                foreach (InteractionObjectPair iop in gameObject.ItemComp.InteractionsInventory)
                {
                    if (iop.InteractionDefinition.GetType() == singleton.GetType())
                    {
                        return;
                    }
                }
            }
            gameObject.AddInteraction(singleton);
            gameObject.AddInventoryInteraction(singleton);
        }

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Virtual gardener
staff: administrator
Original Poster
#19 Old 14th Sep 2020 at 5:38 PM Last edited by Lyralei : 15th Sep 2020 at 10:08 AM. Reason: Now with the actual Socialinteration block
Hi there!

Ah, right, the inventory interactions! I'll add that snippet to the code, thanks again!

I am, however, returning with another issue :p Now it's the case where I made a SocialnteractionA interaction. The Social data XML file loads fine and does return. However, applying said interaction to the pie menu makes it so that whenever I click on the sim, I can't. All you see is the name tooltip.

EDIT: I also refollowed this tutorial: https://modthesims.info/t/492221

I'm currently using a similar method to what Cmar explained in her tutorial: https://modthesims.info/showthread.php?t=491875

Onworldloadfinished:
Code:
            if (Household.ActiveHousehold != null)
            {
                Sim[] objects = Sims3.Gameplay.Queries.GetObjects<Sim>();
                for (int i = 0; i < objects.Length; i++)
                {
                        AddInteractionsSims(objects[i]);
                        // if (sim.Inventory.Find<Phone>() != null)
                        // {
                        // print("Found phone");
                        //Phone phone = sim.Inventory.Find<Phone>();
                        //AddInteractionsPhone(phone);
                        //  }
                }
                EventTracker.AddListener(EventTypeId.kSimInstantiated, OnSimInstantiated);
            }
            else
            {
                EventTracker.AddListener(EventTypeId.kEventSimSelected, OnSimSelected);
            }

      // CODE THAT WE USE TO ADD INTERACTIONS TO SIM
        private static void AddInteractionsSims(Sim sim)
        {
            if (sim.SimDescription.TeenOrAbove)
            {
                foreach (InteractionObjectPair pair in sim.Interactions)
                {
                    if (pair.InteractionDefinition.GetType() == TalkAboutSewing.Singleton.GetType())
                    {
                        return;
                    }
                }
                print("adding talk about sewing");
                sim.AddInteraction(TalkAboutSewing.Singleton);
                print("added talk about sewing");
            }
        }
Event listeners:

Code:
        private static ListenerAction OnSimInstantiated(Event e)
        {
            try
            {
                Sim sim;
                if ((sim = (e.TargetObject as Sim)) != null)
                {
                    AddInteractionsSims(sim);
                }
            }
            catch (Exception ex)
            {
            print("Listener OnSimInstantiated: " + ex.ToString());
            }
      }
        public static ListenerAction OnSimSelected(Event e)
        {
            if (Household.ActiveHousehold != null)
            {
                Sim[] objects = Sims3.Gameplay.Queries.GetObjects<Sim>();
                for (int i = 0; i < objects.Length; i++)
                {
                    AddInteractionsSims(objects[i]);
                }
                EventTracker.AddListener(EventTypeId.kSimInstantiated, OnSimInstantiated);
                return ListenerAction.Remove;
            }
            return ListenerAction.Keep;
        }

And if it helps, the actual interaction:

Code:
    public class TalkAboutSewing : SocialInteractionA
    {
        public class DefinitionTalkAboutSewing : Definition
        {
            public SewingSkill Skill;

            public DefinitionTalkAboutSewing()
                : base("TalkAboutSewingSkill", null, null, false)
            {
            }

            public DefinitionTalkAboutSewing(SewingSkill mSkill)
                : base("TalkAboutSewingSkill", null, null, false)
            {
                Skill = mSkill;
            }

            public override InteractionInstance CreateInstance(ref InteractionInstanceParameters parameters)
            {
                TalkAboutSewing talkAboutSewing = new TalkAboutSewing();
                talkAboutSewing.Init(ref parameters);
                return talkAboutSewing;
            }

            public override void AddInteractions(InteractionObjectPair iop, Sim actor, Sim target, List<InteractionObjectPair> results)
            {
                SewingSkill sewingSkill = actor.SkillManager.GetElement(SewingSkill.kSewingSkillGUID) as SewingSkill;
                results.Add(new InteractionObjectPair(new DefinitionTalkAboutSewing(sewingSkill), iop.Target));
            }

            public override bool Test(Sim a, Sim target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
            {
                int skillLevel = a.SkillManager.GetSkillLevel(SewingSkill.kSewingSkillGUID);

                if (a == target)
                {
                    return false;
                }
                if (skillLevel < 2)
                {
                    return false;
                }
                if (!a.Posture.AllowsNormalSocials())
                {
                    return false;
                }
                if (!target.Posture.AllowsNormalSocials())
                {
                    return false;
                }
                return true;
            }

            public override string GetInteractionName(Sim actor, Sim target, InteractionObjectPair iop)
            {
                return "Talk about sewing test";
            }

            public override string[] GetPath(bool isFemale)
            {
                string text = Localization.LocalizeString("Lyralei/Localized/Socialize:SpecialSewingSkill");
                return new string[1]
                {
                    "Special DEBUGGINGGGGG"
                };
            }
        }
        public static InteractionDefinition Singleton = new Definition();
    }
}
Virtual gardener
staff: administrator
Original Poster
#20 Old 15th Sep 2020 at 12:32 PM Last edited by Lyralei : 15th Sep 2020 at 3:15 PM.
So I came to the conclusion that the 'talkAboutSewing' is actually added *through* the social data. So I think it just makes the pie menu confused about what's happening. Especially since we already define where the talk function is going to be located. So removing 'sim.AddInteractions()' did actually fix it :p

EDIT: That was a lie it seems lol. I worked on the 'Test' function and made it recognisable through XML but it actually doesn't seem to read it for some reason? 

Code:
public static bool TestSewingSkillTalk(Sim a, Sim target, ActiveTopic topic, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
            {
                GlobalOptionsSewingTable.print("test is being used!");
                SewingSkill sewingSkill = target.SkillManager.GetElement(SewingSkill.kSewingSkillGUID) as SewingSkill;
                int skillLevel = target.SkillManager.GetSkillLevel(SewingSkill.kSewingSkillGUID);
                if (sewingSkill != null)
                {
                    if (target.Posture.Container is IBedDouble)
                    {
                        return false;
                    }
                    if (a == target)
                    {
                        return false;
                    }
                    if (skillLevel < 3)
                    {
                        return false;
                    }
                    if (!a.Posture.AllowsNormalSocials())
                    {
                        return false;
                    }
                    if (!target.Posture.AllowsNormalSocials())
                    {
                        return false;
                    }
                    return true;
                }
                return false;
            }
<ProcTest key="Lyralei.TalkAboutSewing,GlobalOptionsSewingTable,TestSewingSkillTalk" /> 

According to my 'try & catch' statement, it does find the actual function (I know that, since I actually misspelt it and that gave me a whole bunch of errors :p) but as you can see, i added a print statement. That one doesn't get triggered. The 'Talk about sewing' also shows up for ALL sims now. 


Secondly, I totally missed that I screwed up the fact that my 'Definition' isn't just called 'Definition' but actually DefinitionTalkAboutSewing : Definition. So therefore 'public static InteractionDefinition Singleton = new DefinitionTalkAboutSewing();' also needs to be 'DefinitionTalkAboutSewing()' rather than 'new Definition' :p. So that fixed the borked pie. but again, the test doesn't want to trigger now :/
Test Subject
#21 Old 16th Sep 2020 at 8:13 PM Last edited by aino : 16th Sep 2020 at 8:29 PM.
I am on my phone and thus code is horrible to read, but I found some things, I wasn't sure about. I haven't had time to get myself familiar with the sims code, so these are just general remarks.

When loading world, you go through sim-objects adding interactions and add listeners. When action fires, the listener code seems to do pretty much the same stuff. Do you really need them both?

When you select sim, what does this line return?
Sim[] objects = Sims3.Gameplay.Queries.GetObjects<Sim>();

If it returns all sims, is it really needed? If you are able to, it would be better for performance to go through smaller subset.

What does this do(, especially that Remove):

EventTracker.AddListener(EventTypeId.kSimInstantiated, OnSimInstantiated);
return ListenerAction.Remove;?

Why do you need to add another listener for when sim is instantiated? Aren't you going to end up with loads of listeners?

Also, as a style remark, if that code was mine, I would get feedback about that long clain of ifs. Perhaps you could combine them like return foo || !bar || foobar;
Instructor
#22 Old 17th Sep 2020 at 1:33 AM Last edited by gamefreak130 : 17th Sep 2020 at 2:46 AM.
Quote: Originally posted by aino
When you select sim, what does this line return?
Sim[] objects = Sims3.Gameplay.Queries.GetObjects<Sim>();

If it returns all sims, is it really needed? If you are able to, it would be better for performance to go through smaller subset.


It does return all Sims, and adding an interaction to all objects/Sims is required because you add interactions to targets, not to actors. If you only added the interaction to a subset of Sims, then all Sims would be able to perform the interaction, but it would only appear when a Sim in that subset is clicked on. Even if this were a reasonable thing to do, I don't think adding interactions to all objects of a given type in the world is as time-consuming or resource-intensive as you think it is.

Quote: Originally posted by aino
What does this do(, especially that Remove):

EventTracker.AddListener(EventTypeId.kSimInstantiated, OnSimInstantiated);
return ListenerAction.Remove;?


The EventTracker class stores and manages EventListener objects. EventListeners typically consist of an EventTypeId (e.g. kSimInstantiated, kMarriage, kBoughtObject, etc.) and a ProcessEventDelegate, which must take an Event as an argument and return either ListenerAction.Keep or ListenerAction.Remove. Event objects come in many different forms based on exactly what in-game affairs they capture, but they all must contain an associated EventTypeId, Actor Sim, and TargetObject.

EventTracker.AddListener() creates a new EventListener from the EventTypeId and ProcessEventDelegate passed to it. When a call to EventTracker.SendEvent() is made, all EventListeners with EventTypeIds matching the sent Event's Id are processed, and their ProcessEventDelegates are called. If the delegate returns ListenerAction.Remove, the associated EventListener is purged from the EventTracker and will not be processed the next time a matching event type is sent. Otherwise, the listener is retained by the EventTracker.

Neither the EventTracker nor anything stored by it is persistable (i.e. saved and loaded by the game), hence why they must be re-added every time a world loads.

Quote: Originally posted by aino
When loading world, you go through sim-objects adding interactions and add listeners. When action fires, the listener code seems to do pretty much the same stuff. Do you really need them both?
Why do you need to add another listener for when sim is instantiated? Aren't you going to end up with loads of listeners?


Sims are fickle things. A Sim object is nothing more than a blank slate; it's the associated SimDescription that contains all the important data regarding appearance, personality, etc. Because of this relationship, the game will often purge Sim objects for various reasons while retaining the SimDescriptions, later "instantiating" them by creating a new Sim object and merging the saved description data onto it. As far as the game is concerned, these re-instantiated Sims are effectively new objects that did not exist when the world was loaded. Thus, we have to listen for Sim instantiation and add the interactions to the newly-recreated Sims when it happens. Again, it's not as much of a performance hit as you might think.

Anyway, @Lyralei I would make sure your SocialData is booting properly by restricting it to, say, just young adults under the "AGE" element of your Action Availability. If that works, then your ProcTest should work (assuming, of course, that your ProcTest is also contained inside the "AA" tag), but even if it doesn't, you should be able to make your current ProcTest into the social interaction's Test override method and achieve the same result now that your Singleton is of the right type.

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Virtual gardener
staff: administrator
Original Poster
#23 Old 17th Sep 2020 at 12:09 PM
Quote:
Also, as a style remark, if that code was mine, I would get feedback about that long clain of ifs. Perhaps you could combine them like return foo || !bar || foobar;
Regards to the last 2 if statements, that's true  and technically, I could indeed turn it into 1 if statement. However, the reason I tend to not do this is really because of debugging reasons. Occasionally I'll go in and compress code or optimize it, once I know it runs without any weird flaws and stuff I hadn't thought of. I do know I should do this more often than I do, but I hope after I'm fully done with the sewing table itself, to have optimized the code in such a way that it is less performance hitting.

(Although most performance hits are actually upon loading the world. Which I did for a reason of course, so that the game doesn't have to do all the big things upon putting the object down, but rather globally.)

Although, not to necessarily defend my horrible statement tendencies (because really if I were to show this and the horrible indentation at my work they'd scream :p) You'll see though how EA's scripts are also sometimes not as 'minified' in a sense. So often you see a variation of a really long if statement, other times you don't. 

Quote:
Even if this were a reasonable thing to do, I don't think adding interactions to all objects of a given type in the world is as time-consuming or resource-intensive as you think it is.
This surprised me too tbh back when I was going through ALL trees in the world, only to add a single object + an interaction. It took the game, upon world load, about 0.56 seconds to execute. mind you, this was for all trees in the world (the university world) and there's a good 200-300 of those in that world  

You are technically right about this in a more programming perspective (at least, I can only say that for web apps though) that looping through ALL the sims is.. not that convenient... Especially if only a particular group of sims that can have that interaction by itself. 

Quote:
Anyway, Lyralei I would make sure your SocialData is booting properly by restricting it to, say, just young adults under the "AGE" element of your Action Availability.
Will try! Hadn't actually tried of doing that yet  I'll let you know how it goes!
Test Subject
#24 Old 17th Sep 2020 at 2:42 PM
Thank you both for answers. And for adding debugging logs, ifs make totally sense. I guess there is no way to attach debugger. That would make life too easy...
Virtual gardener
staff: administrator
Original Poster
#25 Old 18th Sep 2020 at 12:40 PM Last edited by Lyralei : 18th Sep 2020 at 2:47 PM.
@gamefreak130 I think you're right that the Social data is not being parsed correctly. I think what made it show up at first was the old Social availability I added (which I technically don't need if we have a 'test' function). 

So my code says it's being parsed correctly, but clearly it's not :p It seems to parse the 'COM' correctly, which is friendly. The key does come back as "TalkAboutSewingSkill". Which I know is relevant for the STBL (which also works perfect!).

Conclusion, the code only works when I add the social availability XML rather than just the code. WHich is actually in my case not convenient at all. I've attached the social data XML to the post.

I'm almost wondering the ProTest line isn't correct. The Assembly is called GlobalOptionsSewingTable.dll, the namespace + class is Lyralei.TalkAboutSewing and the method is TestSewingSkillTalk. Did I do the fullclassname bit wrong?

EDIT: did some debugging with the 'ActionData.Get()' option and it really does exist in the game, it's just not showing up. I also recreated the 'findMethod' function EA's script uses to split the ProTest line into finding the method we parse in and even that returns back the method 'TestSewingSkillTalk'. 

Page 1 of 2
Back to top