The Dink Network

What's the best way to run a script every time a save is loaded?

February 12th 2012, 11:52 AM
wizardg.gif
Paul
Peasant He/Him United States
 
This is something that's bugged my for a long time, and I think there's no good way to do it, but perhaps I'm wrong. The best way I've found is to have the savebot set a flag (in a global variable) before saving, then unset it again. So if it's set when the savebot's main() runs, we assume they just loaded a game and can launch whatever script(s) we need. It works but...

#1 It's kind of convoluted.
#2 It forces startup stuff to be stuck in an obscure location.
#3 Most seriously, it fails completely if you somehow save without a savebot.

Does anybody know a better way?
February 12th 2012, 12:18 PM
dinkdead.gif
It's pretty much the only way as far as I know...

The only other similar thing I can think of is to create saves with specific numbers which allows you to change stuff on the title screen depending on stuff that happens within the game. I used it in Broken Windows, some others use it too (I think Redink1 and maybe in CC2?), maybe you could extend that idea to get better functionality out of it, but it doesn't help at all with your points #1 and #3...
February 12th 2012, 02:13 PM
wizardg.gif
Paul
Peasant He/Him United States
 
One other idea occurred to me recently, which is to take advantage of the fact that the arm() procedure of the current weapon and magic are run on a load but... That would require altering every item (or more feasibly every magic) and just seems like it could break in a million different ways in a reasonably complex d-mod. So basically that would solve 3, but make 1 and 2 much worse.
February 13th 2012, 05:06 AM
fairy.gif
Someone
Peasant He/Him Australia
 
I don't think there is an elegant solution for running scripts after a load, unfortunately. In MouseDink I did this using the disarm() procedure of Dink's equipped weapon, but this was a special case because the only purpose of assigning Dink an equipped weapon script was to run scripts after loading, and the weapon cannot be disarmed by any means.

I don't remember why I used disarm() and not arm()... judging from my scripts and a vague memory disarm() is run after loading but not after equipping the weapon.

While I don't think an elegant solution exists, I think you can probably come up with a working and even unbreakable but ugly solution with enough testing. It is possible that Dink.exe does something unique on loads (running the disarm and then the arm of the same weapon script might be an example). If it does then you can use that to determine if a load has occurred and run the scripts.

Edit: I looked at the DinkC reference, and it confirms disarm() then arm() is run when loading. It also says that armmovie() is called when the player arms the weapon but not on loading a game or if arm_weapon() is used. So it should be possible to add a bit to each weapon (or each magic) script to detect a load has occurred. Not pretty, but does that matter that much anyway?
February 13th 2012, 02:53 PM
wizardg.gif
Paul
Peasant He/Him United States
 
Wait a minute.

So I'm doing some testing with debug() to see what order arm(), disarm() etc run in, and I notice main.c runs on load_game(), which makes sense actually. Looks promising, but surely there's a catch...

One quick test later and main.c seems perfectly capable of spawning scripts upon loading a game. It would require some extra test to keep it from running on the title screen too, but that should be simple enough.

It can't really be that easy, can it?
February 13th 2012, 03:11 PM
anon.gif
krabby
Ghost They/Them
 
or can it?

I think that's entirely feasible though.
February 13th 2012, 07:03 PM
fairy.gif
Someone
Peasant He/Him Australia
 
I guessed we all overlooked the obvious trying to do something clever
February 13th 2012, 08:14 PM
wizardg.gif
Paul
Peasant He/Him United States
 
I think there may have been some misleading, if not exactly incorrect, info in the original docs that kind of led us astray. For instance it says start.c runs after main.c, this a correct description of what happens when you launch Dink, or do a reset_game(). But nowhere is there any clue that main.c (but not start.c) also runs in a different situation.
February 14th 2012, 01:21 PM
wizardg.gif
Paul
Peasant He/Him United States
 
I knew it. It's not that easy.

Okay, it's almost that easy. But there is one extra complication that may have helped people miss this before. If there is any command that adds a delay in main.c, such as fade_up() or, obviously, wait(), it still works normally on the title screen, but after a load_game(), main.c will be killed when it gets to the wait.
February 15th 2012, 04:44 AM
anon.gif
shevek
Ghost They/Them
 
In "Karel ende Elegast", I have a simple save-system:
- there is only one save-game, no choice.
- the game auto-saves after any cut scene.

Now there are two special situation:
- when continue is attempted but no save file exists, there is a special cut-scene on the title.
- when continue is attempted but the game is already finished, there is a cut-scene on the title... but that's not detectable without actually loading the saved game...

So this is what I did to make it happen:
When finishing the game, Dink is moved to a special room. load_screen() is not called. The game is saved. Dink is moved to a different room (or I quit the game, not sure, you can check for yourself).

This means that during save, nothing special happens. But on loading that game, the main() of this special room is run. That room builds a screen which looks just like the title screen. It plays a cut-scene which is hardly distinguishable from the title, and then calls restart_game() to "continue" inside the title screen. The only visible effect is that the mouse pointer is lost during the cut-scene, and it is warped afterwards.

How can this help you? You can do something similar:
- Create a global variable real_room, or whatever you want to call it.
- On save, set real_room to the current map screen, move Dink to your special room, save, move him back to his current room. Don't call load_screen() at all.
- On load, Dink will be in the special room, its main() script gets run, you do what you want, including moving Dink to his real room. Here you do call load_screen() and draw_screen().

I didn't test it, but I'm pretty sure it should work.
February 15th 2012, 07:50 AM
fairy.gif
Someone
Peasant He/Him Australia
 
re Shevek: Unfortunately that still breaks if the user saves in a way the DMOD author didn't intend (Paul's 3rd point).

re Paul: if main.c is being killed, it sounds like main.c is run before the load game proper occurs, then is killed alongside all other scripts when the load game proper occurs. That's what I thought happens, but I misinterpreted what you said about main.c "keeps running" to mean you found main.c survived the load game. If main.c is run before the load game proper and doesn't survive it, then it seems more than just a complication to me?

Aside from using arm/disarm (which I still think is possible), I've come up with an even more devilishly convoluted solution! It involves randomly generating a number in main.c when a load occurred, saving a game with that number, then trying to recover the last random number generated (assuming Dink's random number generator is predictable) and checking if a save with that number exists. That would allow information to pass from before the load game to after. Probably wouldn't work though

I'm interested to see what you decide is the best solution in the end. Running scripts after a load seems a potential common problem to many DMODs with complex/innovative scripting.
February 15th 2012, 09:24 AM
spike.gif
re Paul: if main.c is being killed, it sounds like main.c is run before the load game proper occurs, then is killed alongside all other scripts when the load game proper occurs.

Yeah, looking at debug.txt, it seems to run somewhere in the ether between using the load_game() command and the game loading up completely. I'm not sure about the reason stuff can't be done in main.c, though; I can't seem to be able to do much whether it's on the title screen or when loading a game. Either stuff doesn't run or the game crashes (even if I just try spawning a script from main.c on the title screen, same result).

However, you can use this to communicate with a saved game! I tried checking for &player_map in main.c, then spiriting the player to another screen if &player_map is anything other than 0 (meaning main.c is being run during the load function, not on the title screen). This works, so you could do something like add a script on the screen Dink is spirited to, then spirit him back to wherever he was when the game was saved. It's a little bit more complicated than doing stuff straight from main.c, but it kills #2 and #3 at least. (I used to use the savebot check method, hopefully I'll never do that again ).
February 15th 2012, 10:39 AM
wizardg.gif
Paul
Peasant He/Him United States
 
I think I must have made that "complication" sound worse than it is. The only thing that happens that is main.c dies when it gets to a wait() or equivalent. Any scripts it has spawned "keep running" as normal (which is what I meant to say, that was a confusing turn of phrase though, I admit).

So what I using right now:
//main.c run when dink is started

void main()
{
  make_global_int("&exp",0);
  make_global_int("&strength", 1);
  make_global_int("&defense", 0);
//blah blah blah

// Some Dink 1.08 enhancements. Not required, but so much nicer.

  set_smooth_follow(1);

// Helper script for loading a save game.
  spawn("loadgame");

// Note: fade_up() has a deley, so main.c will die here if we loaded a game.
// That's why I moved it way down to the bottom.
  fade_up();

  kill_this_task();
}

In case you're wondering, the only reason fade_up is even there is as a safety precaution against the screen staying black if somebody loads new game while faded down.

//loadgame.c is launched from main.c
void main ( void )
{
  if (&player_map == 0)
    kill_this_task();
    //Obviously we're not loading a game, bye bye.

//Note: Nothing weird happens here  ->
  wait(1);

// Launch Status Script
// And this was my main goal, a script that puts some extra info on the status bar using noclip sprites.
  spawn("status");

  kill_this_task();
}

That deal where main.c dies at a wait does make me a little uneasy, but as far as I can tell, this works perfectly. Actually this is a little more complicated than it needs to be, I could have done without loadgame.c, but I wanted to keep things tidy in case I had a bunch more stuff that needs to happen on load.
February 15th 2012, 02:50 PM
spike.gif
I'm not sure why I was having problems before, the crash problem at least was about using a say() command in main.c or something. I guess that's just what I get for doing quick tests. =)

Did you try attaching main.c to 1000? This is another quick test, but the following seems to work just fine both on the title and load game:

script_attach(1000);
wait(500);
say_stop("crap",1);

EDIT: Ahahaha, yeah it's definitely just your regular everyday scripts-dont-survive-screenchanges problem. If you look at debug.txt, the engine first runs main.c, then loads/draws the screen.
February 15th 2012, 06:18 PM
wizardg.gif
Paul
Peasant He/Him United States
 
Ah-hA! Thanks for filling in that final detail. It actually makes some kind of sense now.