- Site Map >
- Modding and Creation >
- Sims 3 Creation >
- Modding Discussion >
- (Scripting) Targeting Sims when Listening for Social Events
- Site Map >
- Modding and Creation >
- Sims 3 Creation >
- Modding Discussion >
- (Scripting) Targeting Sims when Listening for Social Events
Replies: 15 (Who?), Viewed: 520 times.
#1
5th Jul 2022 at 6:09 PM
Posts: 56
Thanks: 295 in 7 Posts
(Scripting) Targeting Sims when Listening for Social Events
I'm getting null interaction errors while listening for social events. While using the reflector, I noticed that socialevent doesn't seem to have targeting. So, how do I detect the actor sim and target sim while using it?
Advertisement
#2
6th Jul 2022 at 9:54 AM
Posts: 3,860
Thanks: 8547 in 67 Posts
Hey Yolksims !
Which Social event? Because it may well just be EA passes it in as a null, but the actor is still actually there.
Because generally speaking, EA tends to do the following:
Meaning, both target/actor has been parsed in.
So I'm wondering it it's maybe the way you're trying to get the actor/target, if you're 100% sure the event you're listening to, sends the right information.
here's how I'd tackle it though:
Which Social event? Because it may well just be EA passes it in as a null, but the actor is still actually there.
Because generally speaking, EA tends to do the following:
Code:
EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, base.Actor, base.Target, this)); EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, base.Target, base.Actor, this));
Meaning, both target/actor has been parsed in.
So I'm wondering it it's maybe the way you're trying to get the actor/target, if you're 100% sure the event you're listening to, sends the right information.
here's how I'd tackle it though:
Code:
public ListenerAction OnSocialization(Event e) { SocialEvent socialEvent = e as SocialEvent; if (socialEvent != null) { // Wouldn't 100% recommend this, since we can't always guarantee that "CreatedSim" won't return null or the Simdescription being able to find it. // Reason for it, is because if a sim isn't in the world (Like a service), then the SimDescription exists, but will return null on CreatedSim. Sim target = SimDescription.Find(socialEvent.mTargetSimDescription.SimDescriptionId).CreatedSim; // This is EA's weird naming convention (but sort of makes sense). While it says "Target" this is actually the Actor. Sim actor = socialEvent.TargetObject as Sim; // It's always good practise to check for null references if (actor != null || target != null) { // DO THING HERE } else { SimpleMessageDialog.Show("Title", "Actor or target were null! Exiting now..."); } } return ListenerAction.Keep; }
#3
6th Jul 2022 at 5:08 PM
Last edited by YolkSims : 7th Jul 2022 at 3:42 AM.
Posts: 56
Thanks: 295 in 7 Posts
Hi Lyralei!
Right now, I'm checking for ANY social event, so I've got
and
After which I'm running a random for random chance of firing and checking the sims against a bunch of conditions, then choosing a romantic social interaction based on those conditions: kiss/first kiss, go steady, propose marriage, woohoo, try for baby, etc.
I should probably mention that I'm not 100% sure it's the target. The code worked with polling (OnUpdate), but now doesn't work now that I'm using a ListenerEvent. I previously had this same problem in this thread, but what I used to fix that isn't working.
Edit: I managed to catch the error again.
Right now, I'm checking for ANY social event, so I've got
Code:
private static void OnWorldLoadFinished(object sender, EventArgs e) { EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(OnInteraction)); }
and
Code:
private static ListenerAction OnInteraction(Event ev) { try { var e = ev as SocialEvent; // Define actor and target sims. Sim Actor = e.Actor as Sim; Sim Target = e.TargetObject as Sim;
After which I'm running a random for random chance of firing and checking the sims against a bunch of conditions, then choosing a romantic social interaction based on those conditions: kiss/first kiss, go steady, propose marriage, woohoo, try for baby, etc.
I should probably mention that I'm not 100% sure it's the target. The code worked with polling (OnUpdate), but now doesn't work now that I'm using a ListenerEvent. I previously had this same problem in this thread, but what I used to fix that isn't working.
Code:
Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Kiss", InteractionPriorityLevel.High, true, false, false); if (kDebugMode == true) { Sims3.UI.StyledNotification.Show(new Sims3.UI.StyledNotification.Format("NPCRomance DEBUG: Initiating Kiss", StyledNotification.NotificationStyle.kSimTalking)); }
Edit: I managed to catch the error again.
#4
6th Jul 2022 at 6:48 PM
Posts: 768
Thanks: 8158 in 12 Posts
after "var e = ..." do a thorough null check.
I also check for !e.WasRecipient to avoid double triggers.
Code:
if (e != null && e.Actor != null && e.TargetObject != null && !e.WasRecipient) { Sim actor = e.Actor as Sim; Sim target = e.TargetObject as Sim;
I also check for !e.WasRecipient to avoid double triggers.
#5
6th Jul 2022 at 8:50 PM
Posts: 56
Thanks: 295 in 7 Posts
Alright, it is that either the target or actor is null. I was able to replicate the error and identify it. Now to figure out how to fix it.
Lyralei's attempt didn't work for me, it's saying it does not contain a definition for mTargetSimDescription.
(Please forgive me for my blunt-minded slowness, I'm having a difficult day.)
Lyralei's attempt didn't work for me, it's saying it does not contain a definition for mTargetSimDescription.
(Please forgive me for my blunt-minded slowness, I'm having a difficult day.)
#6
7th Jul 2022 at 3:41 AM
Last edited by YolkSims : 7th Jul 2022 at 5:31 PM.
Posts: 56
Thanks: 295 in 7 Posts
I'm seriously confused. I've gotten nowhere, and I when I removed all the contents of my script except for a single notification just for shits and giggles, I still got the error.
Code:
private static void OnWorldLoadFinished(object sender, EventArgs e) { EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(OnInteraction)); } private static ListenerAction OnInteraction(Event ev) { try { SocialEvent SEvent = ev as SocialEvent; if (!SEvent.WasRecipient) { // Define actor and target sims. // TargetObject is actually the sim. Sim Actor = SEvent.Actor as Sim; Sim Target = SEvent.TargetObject as Sim; String ActorName = Actor.SimDescription.FullName; String TargetName = Target.SimDescription.FullName; Sims3.UI.StyledNotification.Show(new Sims3.UI.StyledNotification.Format("NPCRomance DEBUG: Social Interaction Detected Between:" + ActorName + TargetName, StyledNotification.NotificationStyle.kSimTalking)); } } catch (Exception exception) { Errorlog.OnError.Exception(exception); } return ListenerAction.Keep; }
#7
7th Jul 2022 at 12:25 PM
Posts: 3,860
Thanks: 8547 in 67 Posts
Ohh I totally missed that indeed the base Event class does actually make mTargetObject the target Somehow my brain didn't think of that... So ignore my weird SimDescription solution thing! :p
Now, EA did a really odd thing? When passing through the target. It first passes it in as a Sim. Then it passes it in as a minisimdescription and then converts it back to a Sim like.... you made a property for this! What are you doing :p
Anyways, getting to the code....
The main thing to always check is if you're not 100% certain something will always be instantiated. Social Event most likely can be null if EA's code screwed up somewhere, so I'd always do a null check there. I used to absolutely trust my code too (or EA's code rather) to always give me something, but nul references are everywhere. And I mean Everywhere.
So take it from me, null reference check anything you can think of Even if you're mainly working with 80% of your own code.
Technically we actually don't know if it's 100% the target returning null. We want to know as well if the actor isn't null. That's what I mean with that you can never be certain that it will always be the sim we're looking for, as hickups and weirdness happen and creating a null reference.
So as Consort suggested, null reference the ever-living heck out of everything (to put it mildly) :p
So for debug reasons I'd also do:
That's how I would've debugged it Secondly, make sure your sims are both in the world. Which they probably are but just mentioning it as the way EA passes in the target can return into a null reference if the sim is still in the relationship tab of your sim, but is a service, or homeless (or in egypt/china/france)
Now, EA did a really odd thing? When passing through the target. It first passes it in as a Sim. Then it passes it in as a minisimdescription and then converts it back to a Sim like.... you made a property for this! What are you doing :p
Anyways, getting to the code....
The main thing to always check is if you're not 100% certain something will always be instantiated. Social Event most likely can be null if EA's code screwed up somewhere, so I'd always do a null check there. I used to absolutely trust my code too (or EA's code rather) to always give me something, but nul references are everywhere. And I mean Everywhere.
So take it from me, null reference check anything you can think of Even if you're mainly working with 80% of your own code.
Technically we actually don't know if it's 100% the target returning null. We want to know as well if the actor isn't null. That's what I mean with that you can never be certain that it will always be the sim we're looking for, as hickups and weirdness happen and creating a null reference.
So as Consort suggested, null reference the ever-living heck out of everything (to put it mildly) :p
So for debug reasons I'd also do:
Code:
private static ListenerAction OnInteraction(Event ev) { try { SocialEvent SEvent = ev as SocialEvent; if (SEvent != null && !SEvent.WasRecipient) { // Define actor and target sims. // TargetObject is actually the sim. Sim Actor = SEvent.mActor as Sim; Sim Target = SEvent.mTargetObject as Sim; // Uncomment this if you are done debugging //if (Actor == null) { return ListenerAction.Keep; } //if (Target == null) { return ListenerAction.Keep; } // Comment the Actor, target, boolean (and the if bool true, then return) once you're certain that you've found your null reference. bool OneReturnedNull = false; if (Actor == null) { SimpleMessageDialog.Show("Title Here", "Actor was null!"); OneReturnedNull = true; } if (Target == null) { SimpleMessageDialog.Show("Title Here", "Target was null!"); OneReturnedNull = true; } // Because we have incomplete data, exit out... if (OneReturnedNull) { return ListenerAction.Keep; } // Technically your old Simdescription code can work too, but name is fine as well! you may get some old Localised names though, especially in bridgeport. string ActorName = Actor.Name; string TargetName = Target.Name; Sims3.UI.StyledNotification.Show(new Sims3.UI.StyledNotification.Format("NPCRomance DEBUG: Social Interaction Detected Between:" + ActorName + TargetName, StyledNotification.NotificationStyle.kSimTalking)); } } catch (Exception exception) { Errorlog.OnError.Exception(exception); } return ListenerAction.Keep; }
That's how I would've debugged it Secondly, make sure your sims are both in the world. Which they probably are but just mentioning it as the way EA passes in the target can return into a null reference if the sim is still in the relationship tab of your sim, but is a service, or homeless (or in egypt/china/france)
#8
7th Jul 2022 at 3:21 PM
Posts: 768
Thanks: 8158 in 12 Posts
Yea Lyralei's code is looking fine. So whats going on seems to be this... Sometimes the game sends 4 events for a social interaction
Let's say Eve flirts with Adam but Adam doesn't like to be flirted with.
First event:
This is a SocialEvent
SEvent.Actor is Eve
SEvent.TargetObject is Adam
SEvent.WasRecipient is false
SEvent.WasAccepted is false
Second event:
This is a SocialEvent
SEvent.Actor is Adam
SEvent.TargetObject is Eve
SEvent.WasRecipient is true
SEvent.WasAccepted is false
Third event:
This is an Event
ev.Actor is Eve
Fourth event:
This is an Event
ev.Actor is Adam
I don't understand why 3 and 4 trigger only sometimes and what is stored in ev.TargetObject. But we dont' really need to do anything with this except filter it out.
Hmm this almost looks like some contractor tried to wrench this in for WorldAdventures where I believe you can make phonecalls to Sims in other worlds which are probably stored as MiniSims.
Let's say Eve flirts with Adam but Adam doesn't like to be flirted with.
First event:
This is a SocialEvent
SEvent.Actor is Eve
SEvent.TargetObject is Adam
SEvent.WasRecipient is false
SEvent.WasAccepted is false
Second event:
This is a SocialEvent
SEvent.Actor is Adam
SEvent.TargetObject is Eve
SEvent.WasRecipient is true
SEvent.WasAccepted is false
Third event:
This is an Event
ev.Actor is Eve
Fourth event:
This is an Event
ev.Actor is Adam
I don't understand why 3 and 4 trigger only sometimes and what is stored in ev.TargetObject. But we dont' really need to do anything with this except filter it out.
Quote:
Now, EA did a really odd thing? When passing through the target. It first passes it in as a Sim. Then it passes it in as a minisimdescription and then converts it back to a Sim like.... you made a property for this! What are you doing :p |
Hmm this almost looks like some contractor tried to wrench this in for WorldAdventures where I believe you can make phonecalls to Sims in other worlds which are probably stored as MiniSims.
#9
7th Jul 2022 at 5:30 PM
Posts: 56
Thanks: 295 in 7 Posts
I don't know what your winky face means, but thank you.
#10
7th Jul 2022 at 6:47 PM
Posts: 56
Thanks: 295 in 7 Posts
Quote: Originally posted by Consort
Yea Lyralei's code is looking fine. So whats going on seems to be this... Sometimes the game sends 4 events for a social interaction Let's say Eve flirts with Adam but Adam doesn't like to be flirted with. First event: This is a SocialEvent SEvent.Actor is Eve SEvent.TargetObject is Adam SEvent.WasRecipient is false SEvent.WasAccepted is false Second event: This is a SocialEvent SEvent.Actor is Adam SEvent.TargetObject is Eve SEvent.WasRecipient is true SEvent.WasAccepted is false Third event: This is an Event ev.Actor is Eve Fourth event: This is an Event ev.Actor is Adam I don't understand why 3 and 4 trigger only sometimes and what is stored in ev.TargetObject. But we dont' really need to do anything with this except filter it out. Hmm this almost looks like some contractor tried to wrench this in for WorldAdventures where I believe you can make phonecalls to Sims in other worlds which are probably stored as MiniSims. |
Thank you. This explains what I was missing in my understanding.
#11
7th Jul 2022 at 9:57 PM
Posts: 56
Thanks: 295 in 7 Posts
I'm still having the problem, and I don't know if it's me being dumb or what. I'm starting to feel like a pain, haha.
I wasn't able to put mActor and mTargetObject because those are private to Event, but my error log seems to be pulling them alright, because they mention IActor and IGameObject.
I still have the code in that's checking if the interaction, actor and target are null. But, something is still managing to slip through my null checks.
It seemed to be working for a while, but now...
Here's the entire code, I've probably messed up again:
I wasn't able to put mActor and mTargetObject because those are private to Event, but my error log seems to be pulling them alright, because they mention IActor and IGameObject.
I still have the code in that's checking if the interaction, actor and target are null. But, something is still managing to slip through my null checks.
It seemed to be working for a while, but now...
Code:
simulating object is null
unable to obtain an interaction object
A null value was found where an object instance was required.
#0: 0x00008 ldfld.o in YolkSims.YolkSims.NPCRomance:Listener (Sims3.Gameplay.EventSystem.Event) ([43E14BB8] )
#1: 0x00000 in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.ProcessEventDelegate:Invoke (Sims3.Gameplay.EventSystem.Event) (424D27D0 [43E14BB8] )
#2: 0x00007 callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.DelegateListener:ProcessEvent (Sims3.Gameplay.EventSystem.Event) (37CD2B60 [43E14BB8] )
#3: 0x00054 callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:ProcessEvent (Sims3.Gameplay.EventSystem.Event) (35EB3C18 [43E14BB8] )
#4: 0x0000e callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:SendEvent (Sims3.Gameplay.EventSystem.Event) ([43E14BB8] )
#5: 0x0000e call in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:SendEvent (Sims3.Gameplay.EventSystem.EventTypeId,Sims3.Gameplay.Interfaces.IActor,Sims3.Gameplay.Interfaces.IGameObject) ([10] [362B4C00] [36776000] )
#6: 0x007e1 call in Sims3.Gameplay.Socializing.Sims3.Gameplay.Socializing.SocialInteractionA:Run () ()
#7: 0x000db stloc.u1 in Sims3.Gameplay.Interactions.Sims3.Gameplay.Interactions.InteractionInstance:RunInteractionWithoutCleanup () ()
#8: 0x00348 stloc.u1 in Sims3.Gameplay.ActorSystems.Sims3.Gameplay.ActorSystems.InteractionQueue:ProcessOneInteraction () ()
#9: 0x0011c stfld.u1 in Sims3.Gameplay.Actors.Sims3.Gameplay.Actors.Sim:DoInteraction () ()
#10: 0x00015 br.s in Sims3.Gameplay.Actors.Sims3.Gameplay.Actors.Sim:Simulate () ()
#11: 0x00022 ldc.i4.0 in ScriptCore.ScriptCore.ScriptProxy:Simulate () ()
Here's the entire code, I've probably messed up again:
Code:
using System; using Sims3.UI; using Sims3.SimIFace; using Sims3.Gameplay.EventSystem; using Sims3.Gameplay.Actors; using Sims3.Gameplay.Interactions; using Sims3.Gameplay.Socializing; namespace YolkSims { public static class NPCRomance { // Removing kInstantiator will cause the script to never load. // Leave it where and as it is. [Tunable] private static bool kInstantiator = false; [Tunable] private static int kChance = 100; [Tunable] private static int kGoOnDate = 10; [Tunable] private static int kKiss = 40; [Tunable] private static int kGoSteady = 50; [Tunable] private static int kWoohoo = 60; [Tunable] private static int kPropose = 70; [Tunable] private static int kTryForBaby = 90; // These are for people who have teen woohoo and marriage enabled. // Or, people who allow their elders to get pregnant. [Tunable] private static bool kProhibitTeens = true; [Tunable] private static bool kProhibitElders = true; // Toggle off certain things. [Tunable] private static bool kAllowTryForBaby = true; // Escape mode. static NPCRomance() { // Trigger the mod to load when the world is finished loading, not before. World.OnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinished); } private static void OnWorldLoadFinished(object sender, EventArgs e) { // When the world is loaded, create a listener to monitor for social interactions. // This is required for the mod to trigger, and has replaced the polling/onupdate method of v1.0.0b. EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(Listener)); } public static ListenerAction Listener(Event ev) { try { // Using ev instead of e opens up access to WasRecipient and WasAccepted. // You can use these to filter out duplicate events (each social event is run at least twice). SocialEvent Interaction = ev as SocialEvent; Sim Actor = Interaction.Actor as Sim; Sim Target = Interaction.TargetObject as Sim; // Filter out null interactions, actors, and targets. // Nulls will always happen, just because of the way EA's code works. Just filter them out. if ((Interaction == null | Actor == null | Target == null) && !Interaction.WasRecipient) { StyledNotification.Show(new Sims3.UI.StyledNotification.Format("Interaction, Actor, or Target was null.", StyledNotification.NotificationStyle.kSimTalking)); return ListenerAction.Keep; } else if (Interaction != null && Actor != null && Target != null && !Interaction.WasRecipient) { // Here the mod gets all relevant information up front and puts them into temporary values. // Get whether actors are blood related. // This pulls from EA's definition of blood relations. Genealogy ActorRelation = Actor.Genealogy; Genealogy TargetRelation = Target.Genealogy; bool AreRelated = Genealogy.IsBloodRelated(ActorRelation, TargetRelation); // Get whether actors are younger than teen, or teens. // There's no need to use teen or below, just use ChildOrBelow and Teen. bool IsActorMinorChild = Actor.SimDescription.ChildOrBelow; bool IsTargetMinorChild = Target.SimDescription.ChildOrBelow; // Get actor ages. // Can be used to keep interactions between peers. bool ActorIsTeen = Actor.SimDescription.Teen; bool TargetIsTeen = Target.SimDescription.Teen; bool ActorIsYA = Actor.SimDescription.YoungAdult; bool TargetIsYA = Target.SimDescription.YoungAdult; bool ActorIsAdult = Actor.SimDescription.Adult; bool TargetIsAdult = Target.SimDescription.Adult; bool ActorIsElder = Actor.SimDescription.Elder; bool TargetIsElder = Target.SimDescription.Elder; // Get sim type. // Can be used to filter out pets, wildlife, and service sims. // Sometimes, service and foreign sims will just return null. bool ActorIsPet = Actor.SimDescription.IsPet; bool TargetIsPet = Target.SimDescription.IsPet; bool ActorIsDeer = Actor.IsDeer; bool TargetIsDeer = Target.IsDeer; bool ActorIsRacoon = Actor.IsRaccoon; bool TargetIsRacoon = Target.IsRaccoon; bool TargetIsServiceSim = Target.SimDescription.IsServicePerson; bool ActorIsServiceSim = Actor.SimDescription.IsServicePerson; // Get sim experience. // Used to choose the correct kiss interaction. bool TargetFirstKiss = Target.SimDescription.HadFirstKiss; bool ActorFirstKiss = Actor.SimDescription.HadFirstKiss; // Get relationship. // OurLTR is used to check the number value of the relationship bar. // OurRelationship is how the sims' relationship is defined, ie. fiancee. float OurLTR = (float)Target.GetRelationship(Actor, false).CurrentLTRLiking; var OurRelationship = Actor.SimDescription.GetRelationship(Target.SimDescription, false); // Get whether sims are on date. // Used to prevent sims from repeatedly asking on date while already in a date. bool ActorOnDate = Actor.IsInGroupOrDateSituation(out bool isActorDate); bool TargetOnDate = Target.IsInGroupOrDateSituation(out bool isTargetDate); // Prevent incest and pedophilia, ensure same age, filter out pets, deer, racoons and service sims. if ((IsActorMinorChild != true && IsTargetMinorChild != true) && (AreRelated != true) && ((ActorIsPet != true && TargetIsPet != true) && (ActorIsDeer != true && TargetIsDeer != true) && (ActorIsRacoon != true && TargetIsRacoon != true)) && ((ActorIsTeen == true && TargetIsTeen == true) | (ActorIsYA == true && TargetIsYA == true) | (ActorIsAdult == true && TargetIsAdult == true) | (ActorIsElder == true && TargetIsElder == true)) && (TargetIsServiceSim != true && ActorIsServiceSim != true)) { // Determine if event will fire. // This is a random chance event, and only less than kChance allows an event to be checked. Random rnd = new Random(); int TotalChance = rnd.Next(0, 100); if (TotalChance < kChance) { // The interactions themselves. // These are checked in order and the first matching is triggered. // Because of this, date is at the end as it has the least strict conditions. // Kiss if (OurLTR >= kKiss && ((OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Friend) | (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.GoodFriend) | (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.BestFriend))) { // Choose the correct kiss according to the target's experience. if (TargetFirstKiss == false) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "First Kiss", InteractionPriorityLevel.High, true, false, false); } else if (TargetFirstKiss == true) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Kiss", InteractionPriorityLevel.High, true, false, false); } } // Go Steady else if (OurLTR >= kGoSteady && OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.RomanticInterest) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Propose Going Steady", InteractionPriorityLevel.High, true, false, false); } // Propose else if (OurLTR >= kPropose && OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Partner) { // If kProhibitTeens is on, prevent teens from proposing. if (kProhibitTeens == true && (ActorIsTeen != true | TargetIsTeen != true)) { return ListenerAction.Keep; } else { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Propose Marriage", InteractionPriorityLevel.High, true, false, false); } } // Woohoo else if (OurLTR >= kWoohoo && (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.RomanticInterest | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Partner | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Spouse)) { // If kProhibitTeens is on, prevent teens from woohooing. if (kProhibitTeens == true && (ActorIsTeen != true | TargetIsTeen != true)) { return ListenerAction.Keep; } else { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "WooHoo", InteractionPriorityLevel.High, true, false, false); } } else if (kAllowTryForBaby == true) { // Try For Baby if (OurLTR >= kTryForBaby && OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Spouse) { // If kProhibitElders is on, prevent elders from trying for baby. if (kProhibitElders == true && (ActorIsElder != true | TargetIsElder != true)) { return ListenerAction.Keep; } else { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Try For Baby", InteractionPriorityLevel.High, true, false, false); } } } // Go on Date else if (OurLTR >= kGoOnDate && (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Acquaintance | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Friend | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.GoodFriend | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.BestFriend)) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Lets Go On Date", InteractionPriorityLevel.High, true, false, false); } else { return ListenerAction.Keep; } } else { return ListenerAction.Keep; } } else { return ListenerAction.Keep; } } } catch (Exception exception) { OnError.Exception(exception); return ListenerAction.Keep; } return ListenerAction.Keep; } } }
Space Pony
#12
7th Jul 2022 at 10:20 PM
Last edited by Battery : 7th Jul 2022 at 10:35 PM.
Posts: 433
Thanks: 792 in 6 Posts
Quote: Originally posted by YolkSims
I'm still having the problem, and I don't know if it's me being dumb or what. I'm starting to feel like a pain, haha. I wasn't able to put mActor and mTargetObject because those are private to Event, but my error log seems to be pulling them alright, because they mention IActor and IGameObject. I still have the code in that's checking if the interaction, actor and target are null. But, something is still managing to slip through my null checks. It seemed to be working for a while, but now...
Code:
simulating object is null Here's the entire code, I've probably messed up again:
Code:
using System; using Sims3.UI; using Sims3.SimIFace; using Sims3.Gameplay.EventSystem; using Sims3.Gameplay.Actors; using Sims3.Gameplay.Interactions; using Sims3.Gameplay.Socializing; namespace YolkSims { public static class NPCRomance { // Removing kInstantiator will cause the script to never load. // Leave it where and as it is. [Tunable] private static bool kInstantiator = false; [Tunable] private static int kChance = 100; [Tunable] private static int kGoOnDate = 10; [Tunable] private static int kKiss = 40; [Tunable] private static int kGoSteady = 50; [Tunable] private static int kWoohoo = 60; [Tunable] private static int kPropose = 70; [Tunable] private static int kTryForBaby = 90; // These are for people who have teen woohoo and marriage enabled. // Or, people who allow their elders to get pregnant. [Tunable] private static bool kProhibitTeens = true; [Tunable] private static bool kProhibitElders = true; // Toggle off certain things. [Tunable] private static bool kAllowTryForBaby = true; // Escape mode. static NPCRomance() { // Trigger the mod to load when the world is finished loading, not before. World.OnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinished); } private static void OnWorldLoadFinished(object sender, EventArgs e) { // When the world is loaded, create a listener to monitor for social interactions. // This is required for the mod to trigger, and has replaced the polling/onupdate method of v1.0.0b. EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(Listener)); } public static ListenerAction Listener(Event ev) { try { // Using ev instead of e opens up access to WasRecipient and WasAccepted. // You can use these to filter out duplicate events (each social event is run at least twice). SocialEvent Interaction = ev as SocialEvent; Sim Actor = Interaction.Actor as Sim; Sim Target = Interaction.TargetObject as Sim; // Filter out null interactions, actors, and targets. // Nulls will always happen, just because of the way EA's code works. Just filter them out. if ((Interaction == null | Actor == null | Target == null) && !Interaction.WasRecipient) { StyledNotification.Show(new Sims3.UI.StyledNotification.Format("Interaction, Actor, or Target was null.", StyledNotification.NotificationStyle.kSimTalking)); return ListenerAction.Keep; } else if (Interaction != null && Actor != null && Target != null && !Interaction.WasRecipient) { // Here the mod gets all relevant information up front and puts them into temporary values. // Get whether actors are blood related. // This pulls from EA's definition of blood relations. Genealogy ActorRelation = Actor.Genealogy; Genealogy TargetRelation = Target.Genealogy; bool AreRelated = Genealogy.IsBloodRelated(ActorRelation, TargetRelation); // Get whether actors are younger than teen, or teens. // There's no need to use teen or below, just use ChildOrBelow and Teen. bool IsActorMinorChild = Actor.SimDescription.ChildOrBelow; bool IsTargetMinorChild = Target.SimDescription.ChildOrBelow; // Get actor ages. // Can be used to keep interactions between peers. bool ActorIsTeen = Actor.SimDescription.Teen; bool TargetIsTeen = Target.SimDescription.Teen; bool ActorIsYA = Actor.SimDescription.YoungAdult; bool TargetIsYA = Target.SimDescription.YoungAdult; bool ActorIsAdult = Actor.SimDescription.Adult; bool TargetIsAdult = Target.SimDescription.Adult; bool ActorIsElder = Actor.SimDescription.Elder; bool TargetIsElder = Target.SimDescription.Elder; // Get sim type. // Can be used to filter out pets, wildlife, and service sims. // Sometimes, service and foreign sims will just return null. bool ActorIsPet = Actor.SimDescription.IsPet; bool TargetIsPet = Target.SimDescription.IsPet; bool ActorIsDeer = Actor.IsDeer; bool TargetIsDeer = Target.IsDeer; bool ActorIsRacoon = Actor.IsRaccoon; bool TargetIsRacoon = Target.IsRaccoon; bool TargetIsServiceSim = Target.SimDescription.IsServicePerson; bool ActorIsServiceSim = Actor.SimDescription.IsServicePerson; // Get sim experience. // Used to choose the correct kiss interaction. bool TargetFirstKiss = Target.SimDescription.HadFirstKiss; bool ActorFirstKiss = Actor.SimDescription.HadFirstKiss; // Get relationship. // OurLTR is used to check the number value of the relationship bar. // OurRelationship is how the sims' relationship is defined, ie. fiancee. float OurLTR = (float)Target.GetRelationship(Actor, false).CurrentLTRLiking; var OurRelationship = Actor.SimDescription.GetRelationship(Target.SimDescription, false); // Get whether sims are on date. // Used to prevent sims from repeatedly asking on date while already in a date. bool ActorOnDate = Actor.IsInGroupOrDateSituation(out bool isActorDate); bool TargetOnDate = Target.IsInGroupOrDateSituation(out bool isTargetDate); // Prevent incest and pedophilia, ensure same age, filter out pets, deer, racoons and service sims. if ((IsActorMinorChild != true && IsTargetMinorChild != true) && (AreRelated != true) && ((ActorIsPet != true && TargetIsPet != true) && (ActorIsDeer != true && TargetIsDeer != true) && (ActorIsRacoon != true && TargetIsRacoon != true)) && ((ActorIsTeen == true && TargetIsTeen == true) | (ActorIsYA == true && TargetIsYA == true) | (ActorIsAdult == true && TargetIsAdult == true) | (ActorIsElder == true && TargetIsElder == true)) && (TargetIsServiceSim != true && ActorIsServiceSim != true)) { // Determine if event will fire. // This is a random chance event, and only less than kChance allows an event to be checked. Random rnd = new Random(); int TotalChance = rnd.Next(0, 100); if (TotalChance < kChance) { // The interactions themselves. // These are checked in order and the first matching is triggered. // Because of this, date is at the end as it has the least strict conditions. // Kiss if (OurLTR >= kKiss && ((OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Friend) | (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.GoodFriend) | (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.BestFriend))) { // Choose the correct kiss according to the target's experience. if (TargetFirstKiss == false) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "First Kiss", InteractionPriorityLevel.High, true, false, false); } else if (TargetFirstKiss == true) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Kiss", InteractionPriorityLevel.High, true, false, false); } } // Go Steady else if (OurLTR >= kGoSteady && OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.RomanticInterest) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Propose Going Steady", InteractionPriorityLevel.High, true, false, false); } // Propose else if (OurLTR >= kPropose && OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Partner) { // If kProhibitTeens is on, prevent teens from proposing. if (kProhibitTeens == true && (ActorIsTeen != true | TargetIsTeen != true)) { return ListenerAction.Keep; } else { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Propose Marriage", InteractionPriorityLevel.High, true, false, false); } } // Woohoo else if (OurLTR >= kWoohoo && (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.RomanticInterest | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Partner | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Spouse)) { // If kProhibitTeens is on, prevent teens from woohooing. if (kProhibitTeens == true && (ActorIsTeen != true | TargetIsTeen != true)) { return ListenerAction.Keep; } else { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "WooHoo", InteractionPriorityLevel.High, true, false, false); } } else if (kAllowTryForBaby == true) { // Try For Baby if (OurLTR >= kTryForBaby && OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Spouse) { // If kProhibitElders is on, prevent elders from trying for baby. if (kProhibitElders == true && (ActorIsElder != true | TargetIsElder != true)) { return ListenerAction.Keep; } else { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Try For Baby", InteractionPriorityLevel.High, true, false, false); } } } // Go on Date else if (OurLTR >= kGoOnDate && (OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Acquaintance | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.Friend | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.GoodFriend | OurRelationship.LTR.CurrentLTR == Sims3.UI.Controller.LongTermRelationshipTypes.BestFriend)) { Target.InteractionQueue.CancelAllInteractions(); Sim.ForceSocial(Target, Actor, "Lets Go On Date", InteractionPriorityLevel.High, true, false, false); } else { return ListenerAction.Keep; } } else { return ListenerAction.Keep; } } else { return ListenerAction.Keep; } } } catch (Exception exception) { OnError.Exception(exception); return ListenerAction.Keep; } return ListenerAction.Keep; } } } |
you try to access Interaction.WasRecipient even if it is null
Code:
if ((Interaction == null | Actor == null | Target == null) && !Interaction.WasRecipient)
solution:
Code:
if ((Interaction == null || Actor == null || Target == null) || !Interaction.WasRecipient)
E: just noticed that you were using Interaction even before this code bit, so like gamefreak said check Interaction for null directly after the cast
#13
7th Jul 2022 at 10:30 PM
Last edited by gamefreak130 : 7th Jul 2022 at 10:44 PM.
Posts: 437
Thanks: 5334 in 22 Posts
One thing that can help you debug issues like this is to look at the stack trace to see exactly where things are failing. Here, the "#0: 0x00008 ldfld.o" means that the function hits an exception really early on (presumably the 8th CIL instruction), which suggests that your SocialEvent is null. (Attempting to access members of a null object throws the NullReferenceException you're seeing.)
Moreover, if you look at SocialInteractionA.Run(), you can see the following lines:
The first two lines will send a SocialEvent as expected, but the second two lines will send a generic Event object with the same EventTypeId (this is what @Consort was referring to earlier). In other words, not all events with the EventTypeId "kSocialInteraction" can necessarily be cast to SocialEvents, and thus you need to do a null check like this:
To make sure that the event you're responding to is in fact a SocialEvent before you try and access anything within it.
Moreover, if you look at SocialInteractionA.Run(), you can see the following lines:
Code:
EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, this.Actor, this.Target, this)); EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, this.Target, this.Actor, this)); EventTracker.SendEvent(EventTypeId.kSocialInteraction, this.Actor, this.Actor.LotCurrent); EventTracker.SendEvent(EventTypeId.kSocialInteraction, this.Target, this.Target.LotCurrent);
The first two lines will send a SocialEvent as expected, but the second two lines will send a generic Event object with the same EventTypeId (this is what @Consort was referring to earlier). In other words, not all events with the EventTypeId "kSocialInteraction" can necessarily be cast to SocialEvents, and thus you need to do a null check like this:
Code:
SocialEvent Interaction = ev as SocialEvent; if (Interaction != null) { Sim Actor = Interaction.Actor as Sim; Sim Target = Interaction.TargetObject as Sim; // etc. }
To make sure that the event you're responding to is in fact a SocialEvent before you try and access anything within it.
"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
If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
#14
8th Jul 2022 at 12:59 AM
Posts: 56
Thanks: 295 in 7 Posts
I'm testing now, but can I ask what the difference is between | and ||?
#15
8th Jul 2022 at 1:49 AM
Posts: 56
Thanks: 295 in 7 Posts
...We have lift off, guys. It's working.
#16
8th Jul 2022 at 2:46 AM
Posts: 437
Thanks: 5334 in 22 Posts
Quote: Originally posted by YolkSims
I'm testing now, but can I ask what the difference is between | and ||? |
The single pipe is a bitwise OR, while two pipes is a conditional OR. You can read more about the differences here.
In many situations they are functionally equivalent, but || is preferred because it has the advantage of short-circuiting, meaning that if the expression on the left hand side is true, then the right hand side will be completely ignored (since "true || x" will always be true, regardless of the value of x). This means that you can do things like:
Code:
if (possiblyNullObj == null || possiblyNullObj.mField == 0) { // Do stuff }
Using a bitwise OR here would throw an exception if possiblyNullObj is in fact null because it would attempt to access mField. With the conditional operator, the right hand side is ignored in this case and the code inside the if block can run.
Note that a similar distinction exists between bitwise AND (&) and logical AND (&&).
"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
If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Who Posted
|