[ZScript] Thinker based Global Variable not saving

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
User avatar
DustedPandemonic
Posts: 82
Joined: Tue Nov 28, 2017 12:17 pm

[ZScript] Thinker based Global Variable not saving

Post by DustedPandemonic »

I'm using the example on this thread as a reference: viewtopic.php?f=3&t=55338

Basically what I'm trying to do is make a progression based RandomSpawner for spawning enemies.
The problem I'm having is the global variable isn't saved when you save and load the game, but everything else works.
I'm not too sure what's going on here, plus I'm not super well versed in ZScript but I understand most of the basic stuff

Attached is an example pk3 with a map to easily test the effects of the spawners.
Attachments
randomspawner_test.pk3
(32.28 KiB) Downloaded 16 times
User avatar
Player701
 
 
Posts: 1710
Joined: Wed May 13, 2009 3:15 am
Graphics Processor: nVidia with Vulkan support
Contact:

Re: [ZScript] Thinker based Global Variable not saving

Post by Player701 »

Now this is very interesting.

The problem is that RandomSpawner chooses an actor to spawn during its BeginPlay call.

What happens is that even before the save file is loaded, the map gets initialized first. Therefore, all RandomSpawners get initialized as well and call BeginPlay. Note that at this point, your thinker hasn't been deserialized yet! So a new instance of it gets created, and then the save file is deserialized, and your previous instance is added on top of that. But since your ThinkerIterator makes only one pass, it returns the wrong instance (the one that got created when the map was loaded).

To fix this, you could modify the code of the Get method so that the iterator finds all instances of your thinker and destroys them all except the last one:

Code: Select all

static MyGlobalVariables Get()
{
    let it = ThinkerIterator.Create('MyGlobalVariables', STAT_STATIC);
    MyGlobalVariables cur, prev = null;
    
    while ((cur = MyGlobalVariables(it.Next())) != null)
    {
        if (prev != null)
        {
            prev.Destroy();
        }
        
        prev = cur;
    }
    
    if (prev == null)
    {
        prev = new('MyGlobalVariables').Init();
    }
    
    return prev;
}
I'm not sure if this is the "true way" to solve this problem because I haven't worked with thinkers a lot. I might have overlooked something, but at least in my tests this approach seems to work.

Another way is to use event handlers instead of thinkers. These are created by the engine and always exist as singletons, i.e. you can be certain there is no more than one instance of each event handler. If you want your variable to be level-scoped, simply use an EventHandler instead of a Thinker. For variables that persist across maps, you will need both an EventHandler and a StaticEventHandler. This is because EventHandler works within a single level but is serialized in save files, while StaticEventHandler can be used to transfer data between different levels but is never serialized. A combination of both should be used to achieve the desired effect.
User avatar
DustedPandemonic
Posts: 82
Joined: Tue Nov 28, 2017 12:17 pm

Re: [ZScript] Thinker based Global Variable not saving

Post by DustedPandemonic »

Interesting, I'll try the fix you posted but I'll also keep the event handler method in consideration. Thanks!
Post Reply

Return to “Scripting”