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:
Timer *MyTimer;
Then I start the timer in a different function:
Mytimer=Timer.Start(120, eOnce);
And I test for expiration in repeatedly execute:
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:
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:
(https://i.imgur.com/8RgET8H.png)
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.
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.
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...
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:
Timer*attacktimer;
Then in the header GlobalScript.ash:
import Timer attacktimer;
And in the script below of room 17:
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
MyTimer.Start(120,eOnce);
?
Yes, that would make it way more comfortable and intuitive to code.
Quote from: TheManInBoots on Fri 03/01/2020 00:10:36
I have in the Global Script body this line:
Timer*attacktimer;
Then in the header GlobalScript.ash:
import Timer attacktimer;
You missed * there in the import.
Also, you need to export the variable in the script body:
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
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
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...
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:
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 :)
Quote from: TheManInBoots on Fri 03/01/2020 19:15:36
I meant obviously like that:
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:
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.
Personally I like it.
And how would "less safe" show itself concretely?
Would it not work only sometimes?
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.
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 :)
Btw, I just thought for a second, would it be possible to add a function like this to the timer script:
function AddAsObject(String name)
{Timer*name;
name=Timer.Create();}
And then you can create the timer object with just one line:
Timer.AddAsObject(MyTimer);
Does that make sense?
No, that wouldn't work, and how is it better than the standard way of creating an object?
Because it takes only one line, not two. So it's easier to write.
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?
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
MyTimer.Start(120,eOnce);
...
MyTimer.Stop();
...
MyTimer2.Start(120,eOnce);
...
etc.
So instead of writing an additional line of
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
Timer*MyTimer;
MyTimer=Timer.Create();
I just write
Timer.CreateAsObject(MyTimer);
That's all lol, that's what the idea was about.
You will be able to do similar thing only if your Timer is a local function variable:
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.
Alright, okay.