What attribute is read by firsttime {} again?

Zesc
04 Oct 2019, 19:31

Greetings fellow Quest users!

I'm currently cleaning up my code, which mostly means defining function to do stuff more efficiently. In once instance, i could spare about 1200 lines of code (yes, indeed) by compressing an firsttime {} otherwise {} script that is used in over 200 pages in a single function.

However, it turns out (as i was afraid) that firsttime[} is triggering only the frist time this script is called... If i recall correctly, firsttime {} does check for an attribute inherent to objects. Atleast i've read that in a different thread. Now i would like to know what this attribute is, and how i can call it from the object that calls the function, and not the function itself?

Thanks in advance, i really appreciate the people who help me in the forum!


mrangel
04 Oct 2019, 20:42

If i recall correctly, firsttime {} does check for an attribute inherent to objects.

Unfortunately not. There is no way to check if a firsttime statement has been fired, because it changes an attribute which is not exposed by the underlying code on the backend.

If you save a game after running a firsttime {} block, then you will notice that the actual script has changed: the firsttime {} block is actually removed from the script, and the corresponding otherwise is replaced by the code that was inside it. This change only happens when the game is saved, though.

A room's "on first enter" scripts use the room's visited attribute. The text processor's {once: directive uses the dictionary game.textprocessor_seen. But the flag for firsttime is only visible to the C# code.


Zesc
05 Oct 2019, 18:56

This makes me sad.

I could use {notfirst:} nested inside of msg () for the otherwise part, but i really need to nest actual script inside of firsttime {} in order for this to work.

Can i check for visited in gamebook mode too? And if so, how to make sure it's checking the attribute of the page that calls the function, and not the function?


mrangel
05 Oct 2019, 21:59

Would it be possible to have the script set a flag when it is run, and use the same flag to see if it's running for the first time?
Slightly more complex than using firsttime, but I can't think of any situation where it wouldn't be possible.

I think the gamebook mode has a different attribute equivalent to visited, but I'm not sure.

EDIT: The attribute is still visited.

how to make sure it's checking the attribute of the page that calls the function, and not the function

Functions don't have attributes. You'd use player.parent to refer to the page the player is on.


Zesc
08 Oct 2019, 18:34

Eh, i'll try sifting through the core libraries if i have time, trying to find out how it is called.

Making the script set a flag for each page, and then checking is one was set there seems... highly inefficient to me. I might save some time copy&pasting (let's be honest, performance and file size aren't a problem), but instead, the game will have to manage about 200 new booleans...

EDIT: i just found out this forum has a reading mode in firefox when you press F9. Like... whoa...


mrangel
08 Oct 2019, 19:10

What are you trying to do that needs so many flags?

If you're looking at copy/pasting that much code, there's very likely an easier way to do it.


Zesc
09 Oct 2019, 19:17

Well, since my game is pretty much open ended in most parts, most pages have a simple script running so i can keep track of the player's progression. (It is also used for a completition percentage.) This is how it looks like:

firsttime {
  player.score = player.score+1
}
otherwise {
  msg ("<i>You've already been here.</i><br>")
}

I probably can shorten the otherwise part with the aforementioned {notfirst:}, but that won't help me with the actual code.

At the moment, this is standing at the beginning of over 200 pages. It is perfectly working, but you can see that this could be done more soigné if i would use a function... but that doesn't work.


mrangel
09 Oct 2019, 20:41

I probably can shorten the otherwise part with the aforementioned {notfirst:},

Not in that case. Because {notfirst: only checks if the game has tried to display that particular message before. So if the same message is on multiple pages, it would check if it's the first time the code has reached any of them.


But if your script is at the start of the page, not inside an if or while block, you can probably do:

if (HasSeenPage (player.parent)) {
  msg ("<i>You've already been here.</i><br>")
}
else {
  player.score = player.score+1
}

That's not the same as using firsttime {} - that script only checks if they've been to the page before. firsttime {} checks if they've reached that part of the script before - so you can put firsttime {} inside an if block, and it will only trigger the first time the condition is met.

(note - HasSeenPage tests if the player has ended their turn on a given page. So if a script on the page sends them somewhere else, it won't realise they've already been there)


Also, is this code on every page? And is it always 1 point per page?

If so, then you can put it in the game.roomenter script ("Script when entering page" on the "Script" tab of the game in the editor). That makes it run for every page without needing to copy/paste it.


Making the script set a flag for each page, and then checking is one was set there seems... highly inefficient to me. I might save some time copy&pasting (let's be honest, performance and file size aren't a problem), but instead, the game will have to manage about 200 new booleans...

The game is managing a boolean for every firsttime it sees anyway. They're just stored within the script object, and not accessible to Quest code.

If you can't use HasSeenPage()/visited for some reason (for example, because the function is called within an if block), then your most efficient solution would be a function that looks like:

if (GetBoolean (player.parent, "score_counted")) {
  msg ("<i>You've already been here.</i><br>")
}
else {
  player.score = player.score+1
}
player.parent.score_counted = true

An extra boolean is literally the smallest possible amount of storage space necessary to record whether or not something has happened.


Zesc
10 Oct 2019, 16:07

HasSeenPage()... this is pretty much everything i've been asking for to get the script, now i can do the rest.

Also, is this code on every page? And is it always 1 point per page?

Nope. I know there is a script to be executed on every page, and already using that handy function. It is used on most, but not all pages... some are reserved for things like the main menu, scripts that redirect the player elsewhere, and other things that have no reason to count to the completition percentage.

An extra boolean is literally the smallest possible amount of storage space necessary to record whether or not something has happened.

So, technically you're right, but let me nitpick: In cases like these, where you have a whole bunch of booleans for a whole bunch of things, there are actually some ways to compress the storage size... not by much, but it certainly isn't practical, but i need to smart-arse my way through life. :P

Anyway, thanks, this is very helpful.