Hi there! You are currently browsing as a guest. Why not create an account? Then you get less ads, can thank creators, post feedback, keep a list of your favourites, and more!
Test Subject
Original Poster
#1 Old 30th Nov 2014 at 9:06 PM
Default Workout / Train other Sim costs Money
Hi,
i've installed this mod -> http://modthesims.info/d/418225
but this don't includes the workout on the training dummy. I try to change this. I want that sims pay for training with the dummy on community lots or pay for training by sim on these, but i don't know what i have to change to get this working Can anybody give me a hint?
Advertisement
Inventor
#2 Old 30th Nov 2014 at 9:21 PM
Hello. I don't know the specifics of that mod but I'm sure that in order to do what
you're asking you have to change its code and hence have a bit of familiarity with
the C# programming language and how TS3 game mechanics works. I don't want
to be rude but I have to ask: do you have any experience with all that?
Instructor
#3 Old 1st Dec 2014 at 9:29 AM
Hopefully, you'll have C# knowledge. Even if you don't, I'd still like to encourage you, as a secondary school student who have learnt C# for Sims 3 just by the tutorials here. It can be overwhelming at first, but just pay attention to each step, and just try to understand each step's purpose. Don't just copy codes and imagine that by changing names, the outcomes will be different. Learn it, but not copy it, and then you'll be able to combine these skills to write up codes.

It should be a Pure Scripting Mod. Ignore the VS setup steps in Buzzler's tutorial, just use Xamarin Studio, recommended below.

The first thing I'd like to help you with is to use Xamarin Studio - it seems to be easier for referencing game .dll files. http://sims.pitchoon.org/monodevelop/XamarinTuto.html

Another thing, is that you can use an EventListener to check whether an event fires. Think of this: when you get hungry, you find food to eat. Getting hungry is the event that fires, and eating is the code that you tell the game "what to do" when such event fires.

The EventListener should be kUsedTrainingDummy
See my profile. I have the Subway Charge mod which works in a much similar way. Download it and export the S3SA resource as .dll, and then decompile it using ILSpy (google it and download, it's free )
Inventor
#4 Old 1st Dec 2014 at 9:50 AM Last edited by Arsil : 1st Dec 2014 at 1:19 PM.
Hey SimsMatthew! Your post is so good that mine wants to woohoo with it.
It was not my intention to discourage the OP, sorry about that.

I'd like to ask you something. I never checked exactly how that works because I never needed to use it,
but I can imagine that using an EventListener you can "intercept" a game event and execute custom
code "anticipating" the official one: in your mod I guess you make sims pay a fee and then the official
code that handles that event is executed normally. But can you also disallow the execution of the official
code? How? Do you have any experience with that? I'm just curious and anyway that can help others
(fer456 was looking for something like this in http://modthesims.info/showthread.php?t=541925).
Test Subject
Original Poster
#5 Old 1st Dec 2014 at 4:13 PM
Ok, thank you for all of these informations. I only have a little bit experience in programming with php, but i think i'm a bit overwhelmed with these .dll-things. I've downloaded ILSpy and decompiled the exported .dll (i have used the s3sa file from the mod-package i've linked in my first post, because this is not so extensive as your s3sa, this made it easier for me to understand the relationships.) Export the code from .dll works fine, but how can i replace the original code with my revised code? Sorry for my stupid question, but i don't have any experience with .dll's before.
Inventor
#6 Old 1st Dec 2014 at 4:49 PM
Before going on, I'd like to ask you something: this modification you want to do is for your personal use only?
Because if you plan to redistribute it then you should first ask and get permission from the author of the mod.
Test Subject
Original Poster
#7 Old 1st Dec 2014 at 5:12 PM Last edited by chazyra : 1st Dec 2014 at 5:40 PM.
it's only for myself. i want to build a dojo and train other sims in martial arts.


edit -->
now i've installed Xamarin Studio and set it up like the tutorial you've linked. i understood now how to create the .dll inclusive all of the requiered sims-libs. But how can i add an additional .cs in the project?
Inventor
#8 Old 1st Dec 2014 at 5:54 PM
You already revised the code? Assuming that, you have to compile the source-code
using Visual Studio or the other IDE software suggested. Compiling it you'll get a .dll
file that you can import in your package (right clicking on the S3SE resource and
selecting "Import DLL").

If you want to use Visual Studio I suggest you to read HERE, if you want to use
Xamarin Studio you better wait for SimsMatthew because I don't know that program.

Recently another user asked how to get started with programming and how TS3 works,
maybe you can find some useful info HERE.

EDIT: ops, just read your edit. In Visual Studio you do something like [Menu]Project->AddExistingItem,
I'm sure the procedure is similar with the other software (maybe you can also drag and drop a file).
Test Subject
Original Poster
#9 Old 1st Dec 2014 at 6:19 PM Last edited by chazyra : 1st Dec 2014 at 6:35 PM.
In Xamarian Studio its add -> add files by right click on the project (so easy, i'm an idiot..) THIS problem so is solved. BUT: no i don't revised the code yet and i don't know how i should begin... can you possibly take a look on the code from mod at first post and tell me how i have to modify?


edit -->
i think i've to copy and modify these code lines:
Code:
private static void World_OnWorldLoadFinishedEventHandler(object sender, EventArgs e)
		{
			EventTracker.AddListener(193, new ProcessEventDelegate(WorkOutCostsMoney.Swimming));
		}
		protected static ListenerAction Swimming(Event e)
		{
			Sim sim = e.get_Actor() as Sim;
			if (sim != null)
			{
				CommonMethods.PayForWorkOut(sim, sim.get_LotCurrent(), CommonMethods.ReturnFee(CommonMethods.WorkOut.Swim));
			}
			return 0;
		}

i think i have to replace "Swimming" with "UsedTrainingDummy", but what have the "193" to say??
Inventor
#10 Old 1st Dec 2014 at 7:27 PM Last edited by Arsil : 1st Dec 2014 at 7:44 PM.
Are you using the last version of the mod (ani_WorkOutCostsMoney_138)?
193 is the id of the event kSwimming.
You better use the code of the workout as model to copy, that seems to me more similar to how the training dummy is handled.

In the method "World_OnWorld..." add a new EventTracker using kUsedTrainingDummy as evendId (354)
Code:
EventTracker.AddListener(EventTypeId.kUsedTrainingDummy, new ProcessEventDelegate(WorkOutCostsMoney.StoppedTrainingDummy));


For the new method, StoppedTrainingDummy (you can call it in another way if you want),
copy/paste from StoppedWorkingOut.

Now we have to modify the new method accordingly. As exercise, try to discover
(with ILSpy) if the training dummy class is derived from the class AthleticGameObject
(TIP: look in "Base types" if you investigate the dummy class or "Derived Types" if you
investigate the other cass). See you later ^^

Quote: Originally posted by Arsil
[CUT]using an EventListener you can "intercept" a game event and execute custom
code "anticipating" the official one[CUT]But can you also disallow the execution of the official
code? How?


In the meantime I think I found the answer. The listener can return ListenerAction.Keep (0)
or ListenerAction.Remove (1). Nope, I was wrong, that return value is only used to remove
or keep the listener (making it a one time thing or a regular thing). Well, I'll figure it out
another time then, no need to fret.
1978 gallons of pancake batter
#11 Old 1st Dec 2014 at 7:55 PM
Quote: Originally posted by Arsil
I never checked exactly how that works because I never needed to use it,
but I can imagine that using an EventListener you can "intercept" a game event and execute custom
code "anticipating" the official one{...}
The listener/tracker system in TS3 implements the observer pattern. That means the actual game action that broadcasts the event has already taken place, or is in the process of taking place. You have no control over the reactions of the other observers that subscribed to the event. Depending on the situation there are a couple ways to change the behavior of other subscribers (that I can think of right now).

1. If you know the subscriber, know what it does and if its action can be reversed, you can fire a OneShotFunction (to make sure that all subscribers have been triggered when your code runs) with a delegate that runs some code to revert the changes made.
2. If you know the exact point where the EventListener has been created and the instance remains, you can remove the listener from the EventTracker manually. May be a little painful if the subscriber re-subscribes on a regular basis.
3. You may be able to completely replace the code that subscribes to the event. That is the most invasive way of course.

If gotcha is all you’ve got, then you’ve got nothing. - Paul Krugman
Test Subject
Original Poster
#12 Old 1st Dec 2014 at 8:06 PM
Thank you very much!
NOW i using the newest version of the mod, thx again
no, the training dummy class is derived from the class GameObject, how do i handle this conflict?
Inventor
#13 Old 1st Dec 2014 at 8:06 PM Last edited by Arsil : 1st Dec 2014 at 8:19 PM.
I see. Thank you Buzzler.

chazyra, sorry for hijacking your thread. I'll behave from now on.

Quote: Originally posted by chazyra
no, the training dummy class is derived from the class GameObject, how do i handle this conflict?


Yep, good job.

Now, the current code check if the object linked to the event is [derived from] a AthleticGameObject,
that's because more than one object can trigger the event and it wont be efficient to check every object
one by one. But that's exactly what we have to do (if you want to make the check also on the...
how is that called... the break rock thingy... whatever), since the two object share no parent class.

Do you want to also include that?

You want to use the same "lot filter" used for the gym machines? I.e. you pay only if you are using them
in a gym or a pool? I don't think so, you said you wanna do a dojo so you'll probably want to include
the WA subLot type (don't remember the name). Answer my questions and start looking how the lot
can be identified (I guess Xamarin has the same auto-completion feature of Visual Studio, so when you
write "LorCurrent." it will show you the possibile choices (fields/methods of that class) if not maybe
you can use ILSpy to search that). See you later.
Test Subject
Original Poster
#14 Old 1st Dec 2014 at 8:29 PM Last edited by chazyra : 1st Dec 2014 at 8:43 PM.
The other object is called BoardBreaker and is also a GameObject. if i want to have pay for the use too i must write an event tracker for this too right?
Lot filter: i thought i create the dojo as gym. when i do that, i have no changes to make about the lot filter, have i?


edit ---> the lot type from wa is kEP1_Dojo, you're right, this fits better

i replace this ->
Code:
(sim.LotCurrent.CommercialLotSubType == CommercialLotSubType.kGym || sim.LotCurrent.CommercialLotSubType == CommercialLotSubType.kPool))

with this -->
Code:
(sim.LotCurrent.CommercialLotSubType == CommercialLotSubType.kEP1_Dojo))

right?
Inventor
#15 Old 1st Dec 2014 at 8:56 PM Last edited by Arsil : 2nd Dec 2014 at 6:36 AM.
All right, since Blender is making me crazy, let's focus on this.

We can put them together, if it's all right to use the same price for both
(well, no, actually we could differentiate the price anyway).
Nope, sorry, that will require another listener for kBrokeBoard, you are
right, I was getting too cocky with this teacher thing, it serves me right :P
We can add that later if you want, let's focus on the training dummy.

We'll use the same payment method of the gym machines, you can
create a new one if you want.

Code:
protected static ListenerAction StoppedWorkingOut(Event e)
{
	Sim sim = e.Actor as Sim;
	if (sim != null)
	{
		TrainingDummy td = e.TargetObject as TrainingDummy;
		if (td != null && sim.LotCurrent != null && sim.LotCurrent.CommercialLotSubType == CommercialLotSubType.kGym)
		{
			CommonMethods.PayForWorkOut(sim, sim.LotCurrent, CommonMethods.ReturnFee(CommonMethods.WorkOut.WorkOut));
		}
	}
	return ListenerAction.Keep;
}


Now, since one and only one object triggers the kUsedTrainingDummy event,
we can probably simplify it like this:

Code:
protected static ListenerAction StoppedWorkingOut(Event e)
{
	Sim sim = e.Actor as Sim;
	if (sim != null && sim.LotCurrent != null && sim.LotCurrent.CommercialLotSubType == CommercialLotSubType.kGym)
	{
		CommonMethods.PayForWorkOut(sim, sim.LotCurrent, CommonMethods.ReturnFee(CommonMethods.WorkOut.WorkOut));
	}
	return ListenerAction.Keep;
}


I haven't tested the code. I'm presuming that the check if the sim is the owner of the lot is made
in the ReturnFee method, it wont make much sense if the proprietor had to pay to use its own stuff.

I gotta go now, I'm glad that I've helped you.
Test Subject
Original Poster
#16 Old 1st Dec 2014 at 9:45 PM
Thanks a lot again! Teaching is better than finished solution
Now i have a listener for kUsedTrainingDummy and one for kUsedBoardBreaker, as well a listener action for StoppedTrainingDummy and one for StoppedBoardBreaker, i've made changes in the common methods file, so that i have yet additional cases for both of them, the xml file is also expanded and the prices are defined. The package file is "tuned" with my stuff.
Tomorrow i'm testing.
Inventor
#17 Old 2nd Dec 2014 at 7:07 AM
I think it's kBrokeBoard, not KUsedBoardBreaker.

I'm feeling a bit uncomfortable for publicly modifying a mod without explicit permission from the author,
especially since we're talking about ani_ and I respect her a lot. What I did was ethically correct?

I feel dirty.

* goes taking 10 showers in a row repeatedly crying "What have I done?" until his voice cracks.
Instructor
#18 Old 2nd Dec 2014 at 9:37 AM
@Arsil, the reason I'd say it's kUsedBoardBreaker, is because I think kBrokeBoard only fires when a Sim succesfully breaks a Martial Arts board (when Sims are practising, they may fail to do so) - re the OP: you'll need some testing to see which works.

Well, ani_ has quit modding according to NRaas, and her original codes are up for modders. So I'd say the OP can go ahead to mod.

BTW curious to know how often do the events fire: in ani_'s case, kSwimming fires per (??) minutes (I forgot), but definitely not just ONCE per interaction. You'll need to test.
Inventor
#19 Old 2nd Dec 2014 at 9:44 AM
That's intentional. You pay based on how much time you swim.
Instructor
#20 Old 2nd Dec 2014 at 12:08 PM
Really? Never knew that. Thanks Arsil!
Test Subject
Original Poster
#21 Old 2nd Dec 2014 at 5:14 PM Last edited by chazyra : 2nd Dec 2014 at 6:14 PM.
The payment is functionally, (using kUsedTrainingDummy and kUsedBoardBreaker) but sims pays not when they stop the training, but almost every second they train (using StoppedTrainingDummy and StoppedBoardBreaker)...

@SimsMatthew: defult pay at swimming by ani_'s mod is every 15 minutes 30$


edit: i think i have to use something like kStoppedTrainingDummy as listener, but this dosen't exists :/

edit: can i define that the sims only pay when they explicite increase their skilllevel in martial arts? event type id for increasing skilllevel is "kSkillLevelUp" but how can i modify it so that only martial arts is affected?
Inventor
#22 Old 2nd Dec 2014 at 7:49 PM Last edited by Arsil : 3rd Dec 2014 at 8:02 AM.
Hihihi, this is when programming becomes interesting (or frustrating).

Probably there are better and more elegant solutions, but two come to my mind:
- apply the payment only every x sim minutes doing a check on the game clock;
- after the first payment activate a cooldown period during which no extra payment
is made (by the same sim) until expiration.

For the first one, we need to know on average how much sim minutes the interaction last,
let's say for example that a training session with the wooden dummy lasts 10 minutes, so
a good choice would be to make the sim pay every 6 sim minutes. How?
Code:
if (((int) SimClock.ElapsedTime(TimeUnit.minutes)) % 6 == 0)

"%" is the module operator, it returns the remainder of an integer division, so if the division
gives 0 as remainder it means that the dividend (the sim minutes) is 6 or a multiple o 6.
CONS
- If the duration of the interactions is very variable this doesn't work well;
- if you manually cancel the interaction you can probably exploit this system;
- sims will pay at a random time during the interaction, not at the start or at the end.

EDIT: if someone curious is wondering, "(int)" is a cast, it's used to "convert" a variable.
The method ElapsedTime(...) returns a float value, which is not good for an integer
division, so we have to convert it first. The type of the value/variable must be somewhat
compatible to the type of value/varialbe we want to convert it to in order to use a cast,
it's not a miracle worker.

For the second solution we could use a moodlet (preferably a hidden one if there's such
a thing) or a variable that keeps track of the last time a sim has paid, there's the matter
of deciding where and how to store such variable, but I bet SimsMatthew has an idea for
that, he goes nuts for dictionaries
Field Researcher
#23 Old 2nd Dec 2014 at 9:07 PM
Quote: Originally posted by Arsil
For the second solution we could use a moodlet (preferably a hidden one if there's such
a thing) or a variable that keeps track of the last time a sim has paid, there's the matter
of deciding where and how to store such variable, but I bet SimsMatthew has an idea for
that, he goes nuts for dictionaries


In my Campaign Fundraiser Online mod, I use a float and a ulong to store the time and the sim, where ulong is the SimDescriptionID. I use them in a class cos I store other values along with them, so in your case what you need is

Code:
Dictionary<ulong, float> _StoredData


To make sure it will persist, it needs to be either a member of a class tagged with the Persistable attribute, or a static field marked with the PersistableStatic attribute.

If you go with the latter, you will also need to add :

Code:
[assembly: PersistableStatic]


That goes in the Assembly Properties, if of course it's not already there.

Nothing's real. Nothing's unreal either.
The frontier between true and untrue is a shady fuzzy line.
Destiny, or maybe the long flight's time-span, shall decide the issue.
Inventor
#24 Old 2nd Dec 2014 at 9:50 PM
Oh, I know you, good to see you here and thanks for contributing ^^

Yep, that sounds good and maybe when/after adding/modifying an entry you can
check for long time expired entries and do some clean-up in the Dictionary.
Field Researcher
#25 Old 2nd Dec 2014 at 10:10 PM
Quote: Originally posted by Arsil
Oh, I know you, good to see you here and thanks for contributing ^^

Yep, that sounds good and maybe when/after adding/modifying an entry you can
check for long time expired entries and do some clean-up in the Dictionary.


It's all in my mod (the cleanup part as well), if you and the OP want to check it out and reuse it, go ahead

P.S. Just for knowledge, the reason I use the SimDescriptionID is because it's the safer way to identify a Sim and it's less data saved. SimDescription and Sim are classes that tend to get mutated or destroyed from time to time, especially when traveling. Also, I don't use Persistable Alarms because Twallan taught us that they're EVIL, so I always recreate all the alarms when the world is loaded and recalculate the time left. That's if the OP wants to use an Alarm to charge the cost

Nothing's real. Nothing's unreal either.
The frontier between true and untrue is a shady fuzzy line.
Destiny, or maybe the long flight's time-span, shall decide the issue.
Page 1 of 2
Back to top