PDA

View Full Version : Need a little help... core/script modding


Therin
8th Mar 2012, 04:57 PM
There was a core mod that I used once which had an option to allow your sims to use the toilet without their clothes on. Obviously without a no censor mod in place that change would serve no purpose...

Sadly that mod will most likely never be updated. I unfortunately at the advice of one of the tutorials on mod management deleted all my unused content and found out that that old core mod is no longer available to even download to see how they dealt with it.

What I'm looking for is some guidance on where I should be looking and what I should change or add in order to make my sims remove their clothes before using the toilet. I'm not worried about the censor or adding parts/skins etc as those types of mods are easily found. I just hate that they sit there in their clothes.

I'd prefer to learn how to script it so that I could use a core mod if I find one that interests me. I want to be able to click on a toilet or urinal and have the following options:
use (clothed -- normal EA method)
use naked (without clothes)

then as some other mods do, have additional options that only show up when cheats are enable. options such as:
autonomous use - all toilets/urinals - clothed
autonomous use - all toilets/urinals - naked

I tried taking the fields entry for mSwitchOutfitHelper from the bathtub listing in the Sims3GameplayObjects.dll and adding it to the toilet and urinal sections but that did nothing. Presumably cause there is nothing listed for going to use the toilet in the list of reasons for ClothesChangeReason under Sims3Gameplay.Actors in Sims3GameplaySystems.dll

Even if I could get it to work, it would be a core mod. meaning I'd have to do the change all over again if I wanted to use a mod which changed something else in the same file. that's why a script mod seems more appealing.

For what it's worth, I have made one working xml tuning mod. my normal sims now have a 50% chance at washing their hands after using the toilet. Found out in my search that non-neat & non-slob sims are treated as slobs in this regard. didn't like that either :P But still, all this .dll & .xml stuff is a bit beyond me.

So..... Any assistance or guidance would be greatly appreciated.

In the meantime, I'm gonna crack open the tutorials once again and see if there isn't anything I've missed.

velocitygrass
8th Mar 2012, 11:49 PM
I imagine that a scripting mod that replaces the use interaction on all toilets/urinals with your own that optionally adds a step where the outfit is changed to naked would do the trick.

So as a first step I'd take a look at Buzzler's Pure Scripting Mod Tutorial.

Therin
9th Mar 2012, 03:04 AM
So as a first step I'd take a look at Buzzler's Pure Scripting Mod Tutorial. been reading and I've even managed to do parts of it. I've gotten the VS component ready for the my 'scripting' part. And the xml file is ready to go when needed. I just have no clue as to what to do from here. Looking at some other scripting mods have left me absolutely clueless. Obviously none of them do what I want so it makes it difficult to understand where I should go from here.

I'm not sure at what point I should start. All I know is that the toilet & urinal stuff seems to be setup inside Sims3GameplayObjects.Plumbing.Toilet & Sims3GameplayObjects.Plumbing.Urinal There is a UseToilet and a UseUrinal both of which seem to be where I'd want to have the change take place but for the life of me I really don't know.

you said to replace the interaction... where does that typically begin at?

Therin
9th Mar 2012, 02:21 PM
I am so lost.... i'm gonna have to give up and hope someone else will decide to do this at some point.

I tried copying the entire UseToilet section and adding in the outfit switch stuff from the bathtub but it wouldn't build in VS cause UseToilet's status makes it inaccessible. you can see it as a greyed out feature in reflector but in VS it doesn't show up at all when you look at the dll file

velocitygrass
10th Mar 2012, 10:37 AM
For being able to access private/protected members/functions, check out this thread (http://www.modthesims.info/showthread.php?p=3766716#post3766716).

Therin
12th Mar 2012, 07:01 AM
well I looked at this again.... finally got VS to build it but after getting it all packaged up nothing happened in game.

I'm sure I'm doing something wrong. I'm not a programmer. Closest I've ever come was basic many many years ago. This C stuff is not readable to me at all.

I can follow instructions. What I can't do is blindly stumble thru hunting and pecking for who knows what and hoping it works. Which unfortunately is what Sims 3 modding is all about.

leesester
12th Mar 2012, 08:17 AM
Therin - I really sympathise with what you are saying - all this C# stuff is confusing to me too. The good news is there is at least a lot of help out there on C# as it is a recognised computer programming language.

I recently had a go at script modding and managed to make an object with an interaction (just using game anims). Do you have reflector or ILSpy? A good idea is to look at the code for something like the bed - that generates a clothes change on entry I think.

Then you would probably need to look at the toilet script and the associated JAZZ and see how to swap it in. The hardest bit (for me) is getting the interaction to run. I made a tutorial on this - it might help.

http://www.modthesims.info/showthread.php?t=469283

EDIT: Actually, the shower. That has a swap clothes on entry as well.

Therin
12th Mar 2012, 07:51 PM
thanks for trying...

I've come to the conclusion that for MY sanity, I have to give up on this. It's causing too much stress... too much stress just to play a game with a little bit of realism to it...

velocitygrass
13th Mar 2012, 03:04 PM
I've uploaded a .zip with a Visual C# 2008 Express project and a package that adds a "Use naked" interaction to all toilets here (http://www.mediafire.com/?2qncje0cdqpiwtt).

Some explanation of the code below:

This adds an event handler, so that the initialization function OnWorldLoadFinishedHandler will be called once the world is loaded.

In that init function we go through all toilets that exist and add our new interaction. We also add a listener for every time a new object is bought. If that happens we check if it's a toilet and if it is, we also add that new interaction.

When adding the interaction to the object we first check if it already exists to avoid duplicates (because when an object is bought the event is sent for every household member). In this case I leave the existing interactions of the object alone, but you could also cycle through them and remove the ones you don't want (e.g. replacing the old one), but since you wanted both options, this example only adds the new option.

The interaction itself is a straight forward simple interaction, that inherits from UseToilet, so that only the minimum needs to be overridden. There's the Run function that is copy and pasted from the original except for two minor additions for undressing and dressing the sim. Note that this is done with a simply switch here. I don't check what the sim was wearing before. I hard coded it to return to Everyday outfit. In the Take Shower interaction, it's done with the OutfitHelper, but I didn't get that to work immediately so, I just replaced it with this single line code.

Finally, you need the Definition that returns the name of your interaction and tests if the Actor can perform it.


using Sims3.Gameplay.EventSystem; // event tracking

using Sims3.Gameplay.Interactions; // new interaction
using Sims3.Gameplay.Autonomy; // doesn't require tuning attribute
using Sims3.Gameplay.Utilities; // alarm manager
using Sims3.Gameplay.Actors; // Sim
using Sims3.SimIFace; // World
using System; // Event handler
using System.Collections.Generic; // List

using Sims3.Gameplay.ActorSystems; // BuffInstance
using Sims3.Gameplay.Core; // RandomUtils
using Sims3.Gameplay.ObjectComponents; // SculptureComponent
using Sims3.Gameplay.Objects.Plumbing; // Toilet
using Sims3.Gameplay.Situations; // Privacy Situation
using Sims3.SimIFace.CAS; // outfit categories


[assembly: Tunable]

namespace Velocitygrass
{
public class UseToiletNakedTest
{
[Tunable]
protected static bool kVelocitygrassScriptTest;


static UseToiletNakedTest()
{
World.OnWorldLoadFinishedEventHandler += new EventHandler(UseToiletNakedTest.OnWorldLoadFinishedHandler);
}

public static void AddToiletInteractions(Toilet obj)
{
// check if the interaction isn't already on the object
foreach (InteractionObjectPair pair in obj.Interactions)
{
if (pair.InteractionDefinition.GetType() == UseToiletNaked.Singleton.GetType())
{
return;
}
}

obj.AddInteraction(UseToiletNaked.Singleton);
}


// This is sent for every member of the household
protected static ListenerAction OnObjectBought(Event e)
{
Toilet targetToilet = e.TargetObject as Toilet;
if (targetToilet != null)
{
AddToiletInteractions(targetToilet);
}

return ListenerAction.Keep;
}

public static void OnWorldLoadFinishedHandler(object sender, EventArgs e)
{
List<Toilet> toilets = new List<Toilet>(Sims3.Gameplay.Queries.GetObjects<Toilet>());
foreach (Toilet toilet in toilets)
{
if (toilet != null)
{
AddToiletInteractions(toilet);
}
}

EventTracker.AddListener(EventTypeId.kBoughtObject, new ProcessEventDelegate(UseToiletNakedTest.OnObjectBought));
}

public sealed class UseToiletNaked : Toilet.UseToilet
{
// Fields
public static readonly new InteractionDefinition Singleton = new Definition();

// Methods
public override bool Run()
{

BuffInstance element = base.Actor.BuffManager.GetElement(BuffNames.ReallyHasToPee);
if ((element != null) && (element.mTimeoutCount <= Toilet.kTimeoutRemainingForBladderEmergency))
{
base.RequestWalkStyle(Sim.WalkStyle.Run);
}
if (!base.Target.Line.WaitForTurn(this, SimQueue.WaitBehavior.DefaultAllowSubstitution, ~(ExitReason.Replan | ExitReason.MidRoutePushRequested | ExitReason.ObjectStateChanged | ExitReason.PlayIdle | ExitReason.MaxSkillPointsReached), Toilet.kTimeToWaitInLine))
{
return false;
}
if (this.CanPerformShooPet)
{
if (!base.Actor.SimRoutingComponent.PreRouteCheckForLine(base.Target))
{
return false;
}
Route r = base.Actor.CreateRoute();
r.PlanToSlot(base.Target, Slot.RoutingSlot_0);
r.RegisterCallback(new RouteCallback(this.SlotInUse), RouteCallbackType.TriggerOnTrue, RouteCallbackConditions.OnEventType(RouteEvent.tEventType.EventDestinationObstructed));
if (!base.Actor.DoRoute(r))
{
return false;
}
if (base.Target.InUse)
{
base.Actor.AddExitReason(ExitReason.ObjectInUse);
return false;
}
}
else if (!base.Actor.RouteToSlotAndCheckInUse(base.Target, Slot.RoutingSlot_0))
{
return false;
}
base.ClearRequestedWalkStyles();
if (!(base.Target is ToiletStall))
{
this.mSituation = Toilet.ToiletSituation.Create(base.Actor, base.Actor.LotCurrent);
}
if (this.mSituation != null)
{
if (!this.mSituation.Start())
{
return false;
}
if (!base.Actor.RouteToSlotAndCheckInUse(base.Target, Slot.RoutingSlot_0))
{
this.mSituation.ExitToiletSituation();
return false;
}
}
base.CancellableByPlayer = false;
base.StandardEntry();
StateMachineClient stateMachine = base.Target.GetStateMachine(base.Actor);
stateMachine.SetParameter("isDirty", base.Target.Cleanable.NeedsToBeCleaned);
this.mCensorEnabled = true;
base.Actor.EnableCensor(Sim.CensorType.LowerBody);
stateMachine.AddOneShotScriptEventHandler(0x78, new SacsEventHandler(this.TurnOffCensorGrid));
stateMachine.AddOneShotScriptEventHandler(0xc8, new SacsEventHandler(this.TriggerTrapCallback));
if (element != null)
{
element.mTimeoutPaused = true;
}


// Undress!
base.Actor.SwitchToOutfitWithSpin(Sim.ClothesChangeReason.GoingToBathe, OutfitCategories.Naked);


if (this.ShouldSit(base.Actor))
{
base.Target.PutDownSeat(stateMachine);
stateMachine.RequestState("x", "peeSitting");
if ((base.Target.SculptureComponent != null) && (base.Target.SculptureComponent.Material == SculptureComponent.SculptureMaterial.Ice))
{
base.Actor.BuffManager.AddElement(BuffNames.Chilly, Origin.FromSittingOnIce);
}
if (base.Target.ToiletTuning.AutoFlushes && RandomUtil.RandomChance((float)Toilet.kChanceOfToiletAutoFlushWhileInUse))
{
stateMachine.RequestState("x", "flushReaction");
}
}
else
{
base.Target.PutUpSeat(stateMachine);
stateMachine.RequestState("x", "peeStanding");
}
base.BeginCommodityUpdate(CommodityKind.Bladder, 0f);
base.BeginCommodityUpdates();
base.Actor.Motives.LerpToFill(this, CommodityKind.Bladder, Toilet.kMaxLengthUseToilet);
base.StartStages();
OccultImaginaryFriend.GrantMilestoneBuff(base.Actor, BuffNames.ImaginaryFriendFeelOfPorcelain, Origin.FromImaginaryFriendFirstTime, true, true, false);
bool succeeded = base.DoLoop(~(ExitReason.Replan | ExitReason.MidRoutePushRequested | ExitReason.ObjectStateChanged | ExitReason.PlayIdle | ExitReason.BuffFailureState | ExitReason.MaxSkillPointsReached | ExitReason.HigherPriorityNext));
base.Actor.BuffManager.UnpauseBuff(BuffNames.ImaginaryFriendFeelOfPorcelain);
base.EndCommodityUpdates(succeeded);
if (succeeded)
{
base.Actor.Motives.GetMotive(CommodityKind.Bladder).PotionBladderDecayOverride = false;
}
if (element != null)
{
element.mTimeoutPaused = false;
}
base.Target.Cleanable.DirtyInc(base.Actor);
if (base.Target.ShouldPutDownSeat(base.Actor))
{
base.Target.PutDownSeat(stateMachine);
}

// Dress!
base.Actor.SwitchToOutfitWithSpin(Sim.ClothesChangeReason.GettingOutOfBath, OutfitCategories.Everyday);

InteractionInstance instance = null;
bool flag2 = base.Target.Line.MemberCount() > 0x1;
if ((this.mSituation == null) || !this.mSituation.SomeoneDidIntrude)
{
if (base.Target.ToiletTuning.AutoFlushes)
{
base.Target.FlushToilet(base.Actor, stateMachine, false);
}
else
{
base.Target.ToiletVolume++;
if (base.Target.ShouldFlush(base.Actor, base.Autonomous))
{
base.Target.FlushToilet(base.Actor, stateMachine, true);
}
}
if (base.Target.ShouldWashHands(base.Actor) && !flag2)
{
Sink target = Toilet.FindClosestSink(base.Actor);
if (target != null)
{
instance = Sink.WashHands.Singleton.CreateInstance(target, base.Actor, base.GetPriority(), false, true);
}
}
}
stateMachine.RequestState("x", "Exit");
if (this.mSituation != null)
{
this.mSituation.ExitToiletSituation();
}
if (flag2)
{
PrivacySituation.RouteToAdjacentRoom(base.Actor);
}
base.StandardExit();
if (instance != null)
{
base.Actor.InteractionQueue.PushAsContinuation(instance, false);
}

return succeeded;
}

// Nested Types
[DoesntRequireTuning]
public sealed new class Definition : InteractionDefinition<Sim, Toilet, UseToiletNakedTest.UseToiletNaked>
{
// Methods
public override string GetInteractionName(Sim a, Toilet target, InteractionObjectPair interaction)
{
return "Use naked";
}

public override bool Test(Sim a, Toilet target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
{
return true;
}
}
}
}
}

Therin
13th Mar 2012, 10:16 PM
thank you. I'll take a look at the project alongside the tutorials and see if I can learn what's going on. Then comment heavily about what's what as I try to do it on my own with this as a guide.

is // the typical single line comment marker in VS ?
I've also seen <!-- --> or something like that. is that the multi-line comment marker?


BuffInstance element = base.Actor.BuffManager.GetElement(BuffNames.ReallyHasToPee);this is one line that gave me grief. reflector returned a negative numerical string with an L on the end. VS wouldn't build cause of it. I couldn't find anything matching in the buff name list. but I was obviously missing a lot of other stuff. I can see that right off the bat.

Buzzler
14th Mar 2012, 06:29 AM
is // the typical single line comment marker in VS ?
I've also seen <!-- --> or something like that. is that the multi-line comment marker?There are three types of comments in C#:
// - single line comments
/* */ - multi line comments
/// - documentation comments

<!-- --> is for XML and HTML
BuffInstance element = base.Actor.BuffManager.GetElement(BuffNames.ReallyHasToPee);this is one line that gave me grief. reflector returned a negative numerical string with an L on the end.The BuffNames enum is based on ulong = unsigned 64Bit integer. The L suffix on the literal means Long. Reflector is unable to determine whether a literal is signed or unsigned and guesses that it's all signed. But of course you can't put a negative value into an unsigned integer. ;)

Anyway, tell Reflector to show numerical values in hexadecimal format and it should work. View -> Options -> Number Format. To determine the human-readable buff name (or any TS3 enum stuff), I usually copy the whole enum definition to Notepad++ and then copy the hex values from the code and Search for them in Notepad++.

Therin
14th Mar 2012, 05:32 PM
from one of the tutorialsI am using TwoBTech for my mods, but you must use something else! Don't use someone else's namespace just like that!

I'm used to modding Infinity Engine games such as Baldur's Gate and there to prevent mod authors from accidentally overwriting each others stuff a prefix reservation list was developed.
Community Filename Prefix Reservation List (http://forums.blackwyrmlair.net/index.php?showtopic=113)
Is there anything similar in the Sims3 modding scene to reserve namespaces? Not that I want to claim one just yet, but if I were to figure this out and try my hand at doing other stuff, I wouldn't want to accidentally use someone's namespace just cause i didn't know it was already being used.

Therin
14th Mar 2012, 06:31 PM
@velocitygrass maybe i'm doing something wrong... idk

I tried building my own script based off of yours. I got down to 2 errors which say
cannot change access modifiers when overriding 'protected' inherited member

so instead I opened up your project in VS and tried to build after pointing the references to my local dll copies.
got the exact same error message

the error messages when clicked on go to the GetInteractionName & Test lines that are at the very end of the script.

I had previously changed all the private/protected things in the Sims3GameplayObjects.dll as was outlined in that thread you linked me to earlier. should I have done that to ALL of the dlls?

Buzzler
14th Mar 2012, 06:47 PM
I'm used to modding Infinity Engine games such as Baldur's Gate and there to prevent mod authors from accidentally overwriting each others stuff a prefix reservation list was developed.{...}Is there anything similar in the Sims3 modding scene to reserve namespaces?Was there some kind of length restriction for BG mod filenames, or something?
We have nothing like that for TS3 mods. The length of namespaces and filenames is basically unrestricted, so just use your name or some other string that is individual to you. I use TwoBTech and used it in the tutorial, because I didn't want to make a bad example. ;)

Therin
14th Mar 2012, 08:25 PM
Was there some kind of length restriction for BG mod filenames, or something?not mod file names. however, actual files added to the game or existing game files that are modified have the max filename length of 8 characters. some file types are restricted further to 7 characters due to the way the game engine handles them. with such limited length, its easy to step on each others toes without something in place to safeguard.

Therin
15th Mar 2012, 01:44 AM
after changing private and family on ALL the dlls it built without any further issues. tested your package and mine in game (not at same time): the sim would strip, do their business, and standup, change into clothes then sit right back down to stand back up.

figuring that something was off in the order of things. I moved this // Dress!
base.Actor.SwitchToOutfitWithSpin(Sim.ClothesChangeReason.GettingOutOfBath, OutfitCategories.Everyday); further down right before return succeeded;Now the sim will strip, do their business, stand up, flush the toilet and then change into clothes. This is an acceptable procedure compared to the earlier funky behavior

as an fyi, I've no plans to publish this...

Thank you for the help in getting it this far.

velocitygrass
15th Mar 2012, 06:32 PM
Strange. For me he changed back into clothes after getting up and before flushing. He didn't sat back down. In any case, I'm glad you got it work.

Therin
18th Mar 2012, 06:49 PM
I need to do additional testing, but i think I successfully managed to make an option where the sim only gets naked when needing to sit on the toilet. So for females that's all the time and males only when their bladder threshold is low enough.

However, I've ran into an interesting scenario. whenever the sims get naked for bath or toilet their hairstyle changes.

It may be default behavior if the CC hair I'm using is not designed for use with the naked outfit. Not sure how to tell that or to allow it if it isn't.

nonamena
18th Mar 2012, 07:13 PM
Open the hair in s3pe, find the CASP resource, click grid, expand ClothingCategory and Naked is on the top of the list. If it's Ffalse, set it to True.

Therin
18th Mar 2012, 08:26 PM
Open the hair in s3pe, find the CASP resource, click grid, expand ClothingCategory and Naked is on the top of the list. If it's Ffalse, set it to True.that was the case with the male hair. can't remember which female hair I had used, but probably wouldn't hurt to go thru each and make sure that they are all set to naked ^^

Thank you for the instructions.