Replies: 2 (Who?), Viewed: 136 times.
Instructor
Original Poster
#1 Old 5th Oct 2021 at 2:04 PM Last edited by echoweaver : 5th Oct 2021 at 10:24 PM.
Default Sending pets to a rabbithole
My pet Fighting skill is in testing, and I'm hung up on the stupidest thing.

I've added an interaction to allow a human to take a wounded cat or dog to the "vet" (i.e. Hospital rabbithole) to treat the wound moodlets. But I can't get the pet to enter the rabbithole. I've used VisitRabbitHoleWith. I've created a custom RabbittHoleInteraction and added the pet as a follower. There's a VisitRabbitHoleWithPet interaction, but that is specific to Opportunities -- maybe the BestInShow opportunity, which involves a human taking their pet to a pet show in the Stadium, except I've been told that the pet doesn't enter the rabbithole THEN either.

Has anyone tried this? I know there isn't a lot of modding going on with pets  Alternately, can anyone think of a game interaction that involves a cat or dog entering a rabbithole that works?

Horses enter the Equestrian Center, but the code for this is pretty specific to RidingPosture or LeadingPosture, so I haven't been able to tease out much from that.

Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Advertisement
Virtual gardener
staff: administrator
#2 Old 5th Oct 2021 at 8:01 PM
Maybe an odd question but when you say 'not going in' do you mean the sim is dropping the pet and then goes in? or does the it cancel alltogether the interaction? I'm actually asking because i'm wondering if at least 'RouteNearEntranceAndIntoBuilding(bool canUseCar, Route.RouteMetaType routeMetaType)' is being fired to make your life easier :p

Looking at the source code though, if I look at the RabbitHoleInteraction interaction's source code, it really seems to only focus on followers or horses. (or just one sim of course :p) and the posture as how the sim is going into the rabbit hole (primarily because of the horse)

If I were you, i'd actually override the Run() function but do this:

Code:
public override Run()
{
          //Pick up pet at all cost!
         ForceSocial(base.Actor, PetOfSim, "Pick Up Pet", base.Priority, false, false, false)

	if (base.Target.EnterSlots.Count != 0 && base.Target.ExitSlots.Count != 0)
	{
                 List<Sim> list = new List<Sim>();
		CarryingPetPosture carryingPetPosture = a.Posture as CarryingPetPosture;
		if (carryingPetPosture != null)
		{
			list.Add(carryingPetPosture .CarriedPet);
		}
        }
        int NumSuccess = 0;
        int NumFail = 0;
	bool flag = false;
	Sim sim = null;
	Route route;

        route = a.CreateRoute();
        int num = kRouteAttemptsToEnterRabbitHole;
        Slot slot = Slot.None;

        // if num stays 1 or lower, then the while loop will be fine, otherwise it will break out as no route could be successfully be planned.
        while (num > 0 && !flag)
	{
		num--;
		try
		{
				base.Target.ActiveEntryRoutes.Add(route);
                                route.SetRouteMetaType(routeMetaType);
				foreach (Slot enterSlot in RabbitHoleProxy.EnterSlots)
				{
					route.AddObjectToIgnoreForRoute(base.Target.SlotToSlotInfo[enterSlot].Footprint.ObjectId);
				}
                                route.PlanToSlot(RabbitHoleProxy, RabbitHoleProxy.EnterSlots.ToArray();
				if (!route.PlanResult.Succeeded())
				{
                                          // For when a route couldn't be successfully be planned.
                                          return false;
				}
                                slot = (Slot)route.PlanResult.mDestSlotNameHash;
                                flag  = base.Target.RouteOutside(base.Actor, null, slot, false, true, false, false);

                                if (!flag && num > 0)
                                {
					if (IntroTutorial.TutorialSim != a)
					{
						base.Actor.RemoveExitReason(ExitReason.RouteFailed);
						if (!base.Actor.HasExitReason(ExitReason.Default))
						{
							base.Actor.LoopIdle();
							float @float = RandomUtil.GetFloat(base.Target.kMinSimMinutesToSleepOnFailedRouteAttempt, base.Target.kMaxSimMinutesToSleepOnFailedRouteAttempt);
							Simulator.Sleep((uint)SimClock.ConvertToTicks(@float, TimeUnit.Minutes));
						}
					}
					break;
                                }
                 }
		finally
		{
		         base.Target.ActiveEntryRoutes.Remove(route);
		}
       }
	if ((a.ExitReason & ExitReason.HigherPriorityNext) == ExitReason.None && (a.ExitReason & ExitReason.UserCanceled) == ExitReason.None)
	{
			if (flag)
			{
				NumSuccess++;
			}
			else
			{
				NumFail++;
			}
	}
       Slot slotToUse = RandomUtil.GetRandomObjectFromList(base.Target.EnterSlots);
       Route routeInsideRabbitHole;
       routeInsideRabbitHole = base.Actor.CreateRoute();
       
       // If you're having some issues getting it to work, uncomment this:
      routeInsideRabbitHole.ExecutionFromNonSimTaskIsSafe = true;

      base.Target.ActiveEntryRoutes.Add(routeInsideRabbitHole);
		routeInsideRabbitHole.SetRouteMetaType(routeMetaType);
		foreach (Slot enterSlot in base.Target.EnterSlots)
		{
			routeInsideRabbitHole.AddObjectToIgnoreForRoute(base.Target.SlotToSlotInfo[enterSlot].Footprint.ObjectId);
		}
		foreach (Slot mountedEnterSlot in base.Target.MountedEnterSlots)
		{
			routeInsideRabbitHole.AddObjectToIgnoreForRoute(RabbitHoleProxy.SlotToSlotInfo[mountedEnterSlot].Footprint.ObjectId);
		}
       bool canEnterRabbitHole = false;

       try
       {
                int numOfFailedAttemptsToGo = 3;
                while (numOfFailedAttemptsToGo > 0 && !canEnterRabbitHole )
                {
                             base.Target.WaitForEnterSlot( RandomUtil.GetRandomObjectFromList(base.Target.EnterSlots), base.actor);
                             numOfFailedAttemptsToGo--;
                             routeInsideRabbitHole.PlanToSlot(base.Target, slotToUse);
                             if(numOfFailedAttemptsToGo == 0)
                             {
                                   routeInsideRabbitHole.DoRouteFail;
                             }
                             canEnterRabbitHole = base.Target.DoRoute(routeInsideRabbitHole)
                             if(!canEnterRabbitHole && numOfFailedAttemptsToGo > 0)
                             {
                                     base.Actor.RemoveExitReason(ExitReason.RouteFailed);
				     if (base.Actor.HasExitReason(ExitReason.Default))
				     {
					   break;
				     }
                                   base.Target.ReservedEnterSlots.Remove(slotToUse);
				   base.Target.ReservedEnterSlotsPerPerson.Remove(slotToUse);
				   base.Target.WaitingToEnterSims[slotToUse].Remove(base.Actor.ObjectId);
                                   base.Target.GetNextSim(slotToUse, base.Actor);
                             }
                 }
       }
       finally
       {
             		base.Target.ActiveEntryRoutes.Remove(route);
			if (base.Target.WaitingToEnterSims.ContainsKey(slotToUse))
			{
				base.Target.WaitingToEnterSims[slotToUse].Remove(base.Actor.ObjectId);
			}
			ObjectGuid a2;
			if (base.Target.ReservedEnterSlotsPerPerson.TryGetValue(slotToUse, out a2) && a2 == base.Actor.ObjectId)
			{
				base.Target.ReservedEnterSlots.Remove(slotToUse);
				base.Target.ReservedEnterSlotsPerPerson.Remove(slotToUse);
				GetNextSim(slotToUse, base.Actor);
			}
       }
	if (!canEnterRabbitHole )
	{
	          return false;
	}

       base.Target.AddToUseList(base.Actor);
	if (PetToUse != null)
	{
		base.Target.AddToUseList(PetToUse);
	}
       int numberOfDoors = base.Target.SlotToSlotInfo[slotToUse].Number;
       base.Target.AnimateDoorOpen(numberOfDoors);

       // if the sim starts animating with a cat or dog glued to their hand in a weird way, it's probably this :p
       base.Actor.PlaySoloAnimation("a2o_rabbithole_enter_x", true);

       base.Actor.FadeOut(true, false);
       PetToUse.FadeOut(true, false);
       base.Actor.SetPosition(base.Target.PositionToHideSimInRabbitHole);
       PetToUse.SetPosition(base.Target.PositionToHideSimInRabbitHole);

      base.Target.AnimateDoorClose(numberOfDoors);
      if (base.Actor.BuffManager.HasAnyElement(BuffNames.HasToPee, BuffNames.HasToPeePet) || PetToUse.BuffManager.HasAnyElement(BuffNames.HasToPee, BuffNames.HasToPeePet))
       {
		base.Actor.Motives.SetMax(CommodityKind.Bladder);
                PetToUse.Motives.SetMax(CommodityKind.Bladder);
       }
      base.Actor.Posture = new InRabbitHolePosture(base.Actor, base.Target);
      base.Actor.SimRoutingComponent.DisableDynamicFootprint();
      base.Actor.RabbitHoleEnvironmentScore = (float)GetInRabbitHoleEnvironmentScore(base.Actor);
      EventTracker.SendEvent(EventTypeId.kEnteredRabbithole, base.Actor, base.Target);
      if (base.Actor.IsSelectable)
      {
		RabbitHoleProxy.StartAmbientSounds(base.Actor, base.Target);
      }
	EventTracker.SendEvent(EventTypeId.kChangedInsideOutsideStatus, base.Actor);
	if (base.Actor!= null)
	{
		EventTracker.SendEvent(EventTypeId.kChangedInsideOutsideStatus, base.Actor);
	}
}


Now if you take this approach, you also will have to manually make an exit exception:

Code:
public override void ExitRabbitHoleAndRouteAway()
{

bool skipWait = false;
	if (!skipWait && !CanSimSkipExitWait(a))
	{
		while (SimClock.ElapsedTime(TimeUnit.Minutes, base.Target.TimeLastSimLeft) < base.Target.RabbitHoleTuning.kNpcExitFreq)
		{
			Simulator.Sleep(30u);
		}
	}
	Base.Target.TimeLastSimLeft = SimClock.CurrentTime();
	Slot slotToUse = GetExitSlot(false);
        Base.target.TimeLastSimLeft = SimClock.CurrentTime();
        int numberOfDoors = base.Target.SlotToSlotInfo[slotToUse].Number;
	try
	{
		Vector3 positionOfSlot = base.Target.GetPositionOfSlot(slotToUse);
		Vector3 forwardOfSlot = Base.target.GetForwardOfSlot(slotToUse);

		base.Actor.SetPosition(positionOfSlot);
		base.Actor.SetForward(forwardOfSlot);
		base.Actor.FadeIn(true);
		PetToUse.SetPosition(positionOfSlot);
		PetToUse.SetForward(forwardOfSlot);
		PetToUse.FadeIn(true);

		base.Target.AnimateDoorOpen(numberOfDoors);
		base.Target.PlayWalkOutAnimation(base.Actor, null, SimRabbitHoleState.Standing);
		base.TargetAnimateDoorClose(numberOfDoors);
	}
	finally
	{
		Base.Target.ReservedExitSlots.Remove(slotToUse);
	}
        base.Target.RemoveFromUseList(base.Actor);
	if (PetToUse != null)
	{
		base.Target.RemoveFromUseList(PetToUse);
	}
       base.Actor.SimRoutingComponent.EnableDynamicFootprint();
       StateMachineClient stateMachineClient = StateMachineClient.Acquire(base.Actor, "PickUpPet");
		stateMachineClient.SetActor("x", base.Actor);
		stateMachineClient.SetActor("y", PetToUse);
		stateMachineClient.EnterState("x", "Enter");
		stateMachineClient.EnterState("y", "Enter");

// If the StateMachine Doesn't like this approach, uncomment the following:
		//stateMachineClient.RequestState(false, "y", "PickUp");
		//stateMachineClient.RequestState(true, "x", "PickUp");

		stateMachineClient.RequestState(false, "x", "Carry");
		stateMachineClient.RequestState(true, "y", "Carry");
      // Makes it so sims should again be holding a pet
     Slots.AttachToSlot(PetToUse.ObjectId, base.Actor.ObjectId, (uint)ContainmentSlots.RightCarry, true, false);
     CarryingPetPosture carryingPetPosture = new CarryingPetPosture(base.Actor, PetToUse, stateMachineClient);
     base.Actor.Posture = carryingPetPosture;
     PetToUse.Posture = posture;
     base.Actor.RabbitHoleEnvironmentScore = -3.40282347E+38f;
     EventTracker.SendEvent(EventTypeId.kExitedRabbithole, base.Actor, base.target);

	PetToUse.SimRoutingComponent.EnableDynamicFootprint();
	PetToUse.RabbitHoleEnvironmentScore = -3.40282347E+38f;
	EventTracker.SendEvent(EventTypeId.kExitedRabbithole, PetToUse, base.Target);

	base.Target.StopAmbientSounds(base.Target);
	EventTracker.SendEvent(EventTypeId.kChangedInsideOutsideStatus, base.Actor);
        base.Target.RouteOutside(base.Actor, null, slotToUse, true, false, true, true);
}


And voila!

Now i'm just Speculating that this code works So if it doesn't work of some null references appear (or the game explodes on you) feel free to let me know! I also just re-wrote a bit of the Routing code the rabbit hole comes with, just so it accepts pets!
Instructor
Original Poster
#3 Old 5th Oct 2021 at 8:53 PM
I don't have the human holding the pet, though I plan to experiment with that. At the moment, both are walking under their own power. I thought that would make the pet a follower. Is that not true? I have been adding the pet as a follower.

The pet and human both are routing to the front of the Rabbithole. The human goes in. The pet drops the interaction and wanders off (usually to take a nap).

In addition to creating an interaction that inherits from RabbitHoleInteraction, I also tried instantiating VisitRabbitholeWith. The VisitRabbitholeWithBase class loops through followers and directs each one to enter the Rabbithole, but this has the same behavior -- pet routes to the Rabbithole, then drops queue.

I like the idea of the human carrying the pet into the Rabbithole. I'll need to take some time to look over your suggested code here. I did see when I was picking through the EA interactions that there were references to CarryingChildPosture. CarryingPetPosture isn't referenced (sigh), but I imagine I could override methods and add code for that.

However, I'm pretty sure you can't carry large dogs. Is there a dog-walk posture for walking dogs on a leash? I wonder if that could be used.

Echo Weaver's Simblr: http://echoweaver.tumblr.com/
A portrait in stubbornness - Playing the same legacy since 2009
Sample a Brave Legacy: http://sims3sample.illation.net
Back to top