PDA

View Full Version : C# adding alarms to sims question


Consort
8th Apr 2012, 12:01 AM
I'm making a "pure" scripting mod that acually works nicelyish but to fix some bugs I think I need to understand a few things...

I want to add a timed alarm to a sim.
I also want the called method to know about the Sim object I added the alarm to.


What I'm doing looks like this (my code from a from a ChairLounge object):

AlarmHandle TanAlarm = base.Actor.AddAlarm (5f, TimeUnit.Minutes, new AlarmTimerCallback(StartTan), "blah", AlarmType.DeleteOnReset);

base.Actor is of type Sim.

How does my "StartTan" method that gets called by the alarm find out what Sim object is calling?
I thought I could pass the Sim object via StartTan(base.Actor) or something but c# doesn't let me. Is that supposed to work or impossible due to the delegate nature of the method being called?

I am still pretty confused by alarms and events and handlers and delegates and so on. I hope someone can point me into the right direction.

misukisu
8th Apr 2012, 08:07 AM
AlarmTimerCallback defines the delegate method parameters, so if it does not pass the alarm owner you will have to find some other way to get it. Have you looked at the World AlarmManager alarms yet?

I think it might be also possible to make some helper class to deliver information, then used like this (just the middle part of the code you pasted):
new AlarmTimerCallback(new AlarmCollector(base.Actor).StartTan)

where the AlarmCollector would be your new helper class, having the actor as it own field where it can get it after the StartTan event comes in.

CmarNYC
8th Apr 2012, 04:12 PM
I'd been wanting a way to do this for some time, and your thread pointed me in the right direction. :)

Try defining your alarm this way, with a "this." reference to the callback method:

Target.AddAlarm(10f, TimeUnit.Minutes, new AlarmTimerCallback(this.TimerTarget), "Test Timer", AlarmType.DeleteOnReset);

And your callback this way within the same class (using a notification as an example):

private void TimerTarget()
{
StyledNotification.Show(new StyledNotification.Format(base.Actor.Name + ", " + base.TargetActorSimDescription.FullName,
StyledNotification.NotificationStyle.kGameMessagePositive));
}

You can get the actor sim with the base.Actor reference. Since I set it up as a pie menu option I can also get the target sim. Another tricky part (for me anyway) is that the alarm timer seems to be based on sim time, not real time, so 5 seconds expired almost instantly in the real world.

Consort
8th Apr 2012, 07:11 PM
Ok thanks, now I can stop searching for a thing that does't even exist :)

@misukisu
The idea of some form of helper class sounds like an elegant idea. I wonder what happens to it when the game is saved?
I had a quick look at some of the thingys that have "alarm" in it but my first impression was that they more or less do the same thing? Really not sure,

@Cmar
Yea that's how I'm doing it (you don't need the "this" I think), It just felt very "hacky". I was also afraid that it would cause another problem...

You see I'm making this interaction for lounge chairs so sims can get tanned in the sun.

The tanning procedure is fairly straight forward: Sim in chair, chair tans sim.
However the untanning takes place when the sim has left the chair and possibly a different sim would be tanning in it. I was afraid things would not work because both are Base.Actor, but to my surprise it's working nicely,

misukisu
9th Apr 2012, 05:42 AM
If the sim alarms are saved then it should work all right, as the reference to the wanted object is there.

But the way CmarNYC suggested is better, simpler. And as you are not forwarding the delegate call to singleton, but to the Interaction instance you will not have the problem that you were worried about. You see, the delegate call goes to the instance that creates the AlarmTimerCallback, which has the correct sim as its actor.

Buzzler
16th Apr 2012, 12:40 PM
The idea of some form of helper class sounds like an elegant idea. I wonder what happens to it when the game is saved?To make objects of a custom class survive save&reload, you need to add the [Persistable] attribute to the class and the class needs a parameterless constructor.

Consort
19th Apr 2012, 01:37 AM
@Buzzler thats pretty cool... what I'd need is a persistent dictionary... i googled and found one of your old posts here http://nene.modthesims.info/showthread.php?t=435218

How do I initialize such a dictionary?
I would need a new constructor somewhere.. but that wipes the dictionary...

so, something along the lines of..

if (mChokers == null) {mChokers = new Dictionary<ulong, Choker>}

Or rather by a onFirstLoad function that contains the constructor?
Or am I on the wrong track completely?

Buzzler
19th Apr 2012, 08:19 AM
How do I initialize such a dictionary?{...}

if (mChokers == null) {mChokers = new Dictionary<ulong, Choker>}This pretty much. All non-static fields are persistable by default in persistable classes btw.

Something you need to look out for are static fields. You need to use the [PersistableStatic] attribute on them and always make sure to nullify them OnWorldQuit (if the main menu is loading) so they won't be transferred to other worlds if the player loads another savegame.

Consort
20th Apr 2012, 07:28 PM
Mh ok, this is my shot at it. I'm not surprised that it doesn't save anything...
I think I have not understood the whole concept... @Buzzler can you recommend any code I could read to see how it's done?


I'm making a static field for the class instance:
namespace ConsortMod
{
public class Sunbath
{
[Tunable]
protected static bool kInstantiator = false;
private static SavableAlarmManager tanAlarmManager;
...
Uhm do I need this on worldload?
private static void OnWorldLoadFinished(object sender, EventArgs e)
{
tanAlarmManager = new SavableAlarmManager();
...
}

...
The class containing the dictionary I want persistable
[Persistable]
public class SavableAlarmManager
{
private Dictionary<Sim, AlarmHandle> AlarmDict ;

public SavableAlarmManager()
{
if (AlarmDict == null)
{
AlarmDict = new Dictionary<Sim, AlarmHandle>();
}
}
public void AddRemoveSimAlarm(float time, TimeUnit unitOfTime, AlarmTimerCallback func, AlarmType alarmType, Sim sim)
{
if (AlarmDict.ContainsKey(sim))
{
AlarmManager.Global.RemoveAlarm(AlarmDict[sim]);
AlarmDict[sim] = AlarmManager.Global.AddAlarm(time, unitOfTime, func, "untan", alarmType, sim);
}
else
{
AlarmDict.Add(sim, AlarmManager.Global.AddAlarm(time, unitOfTime, func, "untan", alarmType, sim));

}
}
public void status()
{
String msg = "";
{
foreach (KeyValuePair<Sim,AlarmHandle> pair in AlarmDict)
{
msg += (pair.Key.Name + "|");
}
Notify(msg +" DictSize: " + AlarmDict.Count);
}
}

}

Consort
22nd Apr 2012, 02:03 AM
Just found out that PersistableStatic only works when you turn it on in the assembyinfo.cs :)
Took me only 3 hours \o/