Problem using Crimson Wizard's Timer Module: Timers work within function only

Started by TheManInBoots, Thu 02/01/2020 22:22:37

Previous topic - Next topic

TheManInBoots

Hi!

I was trying to use Crimson Wizard's Module "Timer 0.9", mainly so I can use more than 20 timers.

However the timer's name is only recognized within the function in which I defined the Timer.

For example:

I define the timer in global script in the game_start function:

Code: ags
Timer *MyTimer;


Then I start the timer in a different function:

Code: ags
Mytimer=Timer.Start(120, eOnce);


And I test for expiration in repeatedly execute:

Code: ags
if(Timer.IsExpired(Mytimer))
{}


All exactly as in the Module description. However the engine does not remember the "Mytimer" definition outside the function so I get an error:

Code: ags
GlobalScript.asc(494): Error (line 494): Undefined token 'Mytimer' 


I can place all the functions within the game_start function and the engine recognizes the defined term. Obviously that makes the timer unusable.

Is there something I'm not seeing here?

I imported the module normally into the scripts:



Just in case it matters I had a "pause" bool in Global variables that I eventually changed to "pause2" because the Time0.9 Module has defined something as "pause" as well.

Crimson Wizard

Because these timers are referenced through variables, not through "names", you must work with them as with any other variables. The variables are only seen in the "block" they are declared:
* if variable is declared inside a function, then it may be only used inside that function;
* if variable is declared outside a function in the script body, then it may be used inside that script body;
* if variable is declared as import in the script header (or on Global Variables pane), it then may be used in any script below that header.

Also, idk if it's a typo or it's really in your code, but you said you declared it as "Timer *MyTimer" and then used as "Mytimer". AGS script is case sensitive, so that would be a different variable name.

Quote from: TheManInBoots on Thu 02/01/2020 22:22:37
Just in case it matters I had a "pause" bool in Global variables that I eventually changed to "pause2" because the Time0.9 Module has defined something as "pause" as well.

Hmm that does not sound right. Maybe it's another bug in AGS script (i thought something similar was fixed earlier). I will check this out.

Crimson Wizard

Thinking about this, it may be possible to support Timer's name and getting Timer by name, as a feature for the next version of this module...

TheManInBoots

Quote from: Crimson Wizard on Thu 02/01/2020 22:37:22
* if variable is declared outside a function in the script body, then it may be used inside that script body;
OK, I assume I didn't try right in the beginning. That works fine, thank you.

Quote from: Crimson Wizard on Thu 02/01/2020 22:37:22
* if variable is declared as import in the script header (or on Global Variables pane), it then may be used in any script below that header.
However, that isn't working.

I have in the Global Script body this line:
Code: ags
Timer*attacktimer;


Then in the header GlobalScript.ash:
Code: ags
import Timer attacktimer;


And in the script below of room 17:
Code: ags
attacktimer=Timer.Start(120, eOnce);


And I receive this error message:
QuoteFailed to save room room17.crm; details below
room17.asc(29): Error (line 29): cannot assign to 'attacktimer'


Also, is there actually a way to add Timer* types to Global Variables? It's not in the type list.

Quote from: Crimson Wizard on Thu 02/01/2020 22:37:22
Also, idk if it's a typo or it's really in your code
Yeah lol, it's a typo, I copy and pasted from your Module description I think, but it wasn't like that in the script.

Quote from: Crimson Wizard on Thu 02/01/2020 22:37:22
Maybe it's another bug in AGS script (i thought something similar was fixed earlier). I will check this out.
I am using AGS 3.5.0.22, so not the most recent version. I received an error message that said "pause" in timer script can't be defined, because it's already defined.

Quote from: Crimson Wizard on Thu 02/01/2020 22:41:18
Thinking about this, it may be possible to support Timer's name and getting Timer by name, as a feature for the next version of this module...
You mean for example like
Code: ags
MyTimer.Start(120,eOnce);
?

Yes, that would make it way more comfortable and intuitive to code.

Crimson Wizard

Quote from: TheManInBoots on Fri 03/01/2020 00:10:36
I have in the Global Script body this line:
Code: ags
Timer*attacktimer;


Then in the header GlobalScript.ash:
Code: ags
import Timer attacktimer;


You missed * there in the import.
Also, you need to export the variable in the script body:
Code: ags

export attacktimer;



Quote from: TheManInBoots on Fri 03/01/2020 00:10:36
Also, is there actually a way to add Timer* types to Global Variables? It's not in the type list.

Hm, no, unfortunately custom types cannot be chosen there, which is another missing feature.


Quote from: TheManInBoots on Fri 03/01/2020 00:10:36
Quote from: Crimson Wizard on Thu 02/01/2020 22:41:18
Thinking about this, it may be possible to support Timer's name and getting Timer by name, as a feature for the next version of this module...
You mean for example like
Code: ags
MyTimer.Start(120,eOnce);
?

Yes, that would make it way more comfortable and intuitive to code.

No, I meant, getting timer pointer by name instead of having to import/export global variable, like
Code: ags

Timer* t = Timer.GetByName("name");



Regarding the way you suggested, if such function existed you would have to still create a timer object first, and it will be less safe, since it's a pointer and may appear to be null. I could add this as an option though.

Alternatively, I'd have to redo all the module and make timers non-pointers, which may be possible but have its own complications...

TheManInBoots

Quote from: Crimson Wizard on Fri 03/01/2020 00:21:28
You missed * there in the import.
Also, you need to export the variable in the script body:
Neat! It works just fine now.

Quote from: Crimson Wizard on Fri 03/01/2020 00:21:28
No, I meant, getting timer pointer by name instead of having to import/export global variable, like
Sure, that'd be easier!

Quote from: Crimson Wizard on Fri 03/01/2020 00:21:28
Regarding the way you suggested, if such function existed you would have to still create a timer object first, and it will be less safe, since it's a pointer and may appear to be null. I could add this as an option though.

Alternatively, I'd have to redo all the module and make timers non-pointers, which may be possible but have its own complications...

Oh, please don't do it then. That sounds like a lot of work!

I meant obviously like that:
Code: ags

Timer*MyTimer;

MyTimer.Start(120,eOnce);

So I though ,-if reading the name as a timer is less safe, it could automatically infer "=Timer". But I don't know how it works, and again, if that's a lot of work don't bother. Your module is already easy enough to use as it is  :)

Crimson Wizard

Quote from: TheManInBoots on Fri 03/01/2020 19:15:36
I meant obviously like that:
Code: ags

Timer*MyTimer;

MyTimer.Start(120,eOnce);

So I thought after the Timer is pointed to the string name it could read the name as a timer or automatically infer "=Timer". But I don't know how it works, and again, if that's a lot of work don't bother. Your module is already easy enough to use as it is  :)



To elaborate, I could maybe add following functions, as an optional way to create and work with timer:
Code: ags

Timer*MyTimer;
...
MyTimer = Timer.Create();
...
MyTimer.Start(120,eOnce);
...
MyTimer.Stop();


This is technically trivial, I think, but also slightly less safe. But then again, everyone is using DynamicSprites and Overlays that way, so maybe not a very bad idea.

TheManInBoots

Personally I like it.
And how would "less safe" show itself concretely?
Would it not work only sometimes?

Crimson Wizard

Quote from: TheManInBoots on Fri 03/01/2020 19:24:21
And how would "less safe" show itself concretely?
Would it not work only sometimes?

It's less safe, because if you forget to create timer or set MyTimer = null, and then try to do MyTimer.Start, it will crash with null pointer error :).
Current static methods check if pointer is null automatically.
Although maybe I am overthinking this. And someone may argue it's better to make it crash because that would signify a logic error in program.

TheManInBoots

Seems safe enough to me! As long as you code correctly, it works- that's as safe as the rest of AGS. Do as you see fit, and thanks for the help  :)

TheManInBoots

Btw, I just thought for a second, would it be possible to add a function like this to the timer script:

Code: ags
function AddAsObject(String name)

{Timer*name;
name=Timer.Create();}


And then you can create the timer object with just one line:

Code: ags
Timer.AddAsObject(MyTimer);


Does that make sense?

Snarky

No, that wouldn't work, and how is it better than the standard way of creating an object?

TheManInBoots

Because it takes only one line, not two. So it's easier to write.

Crimson Wizard

This particular syntax would not work, because in AGS script you cannot convert variable name into string like that.

But on another hand, I don't quite understand how such function is supposed to be used anyway. Could you give a hypothetical example?

TheManInBoots

Let's say, I want to create several Timers, and I know from the beginning that I prefer using all of them as objects.
So that I can script
Code: ags
MyTimer.Start(120,eOnce);
...
MyTimer.Stop();
...
MyTimer2.Start(120,eOnce);
...
etc.


So instead of writing an additional line of
Code: ags
MyTimer.Create();

for every single Timer I basically create the timer object from the very beginning in one line. Just because it's faster.

Instead of
Code: ags
Timer*MyTimer;
MyTimer=Timer.Create();

I just write
Code: ags
Timer.CreateAsObject(MyTimer);

That's all lol, that's what the idea was about.

Crimson Wizard

You will be able to do similar thing only if your Timer is a local function variable:
Code: ags

Timer*MyTimer = Timer.Create();


But you won't be able to do this with a global variable, because, on one hand you must have a variable declaration somewhere outside the function (Timer* MyTimer), and on another hand you cannot run timer creation outside the function and will have to do it in some function. So these will be separated anyway.

TheManInBoots

Alright, okay.

SMF spam blocked by CleanTalk