Page 1 of 1

mission.runScreen and callback

Posted: Thu Jul 22, 2010 9:36 am
by Commander McLane
I am still trying to get my head around the new and changed methods in Oolite 1.74, in order to update my OXPs.

One major change is the new mission screen handling, and the introduction of the callback function for mission screens.

Before I go and translate a lot of code in my OXPs with a lot of mission screens, I have a question. For the sake of simplicity (and laziness, and keeping mission screens together in my script), I want to change as little as possible in the general structure of my script. Therefore I'd like to know whether the following code would work, or whether calling the own mother function as a call back would be able to bring about the end of the world as we know it (actually: some sort of stack overflow and crash):

Code: Select all

this.missionScreens = function()
{
	if(!missionVariables.mymission && system.ID == 100)
	{
		mission.runScreen({messageKey:"mymission_message1a", choicesKey:"mymission_continue"}, this.missionScreens);
		missionVariables.mymission = "STAGE1a";
	}
	else if(missionVariables.mymission == "STAGE1a")
	{
		mission.runScreen({messageKey:"mymission_message1b", choicesKey:"mymission_continue"}, this.missionScreens);
		missionVariables.mymission = "STAGE1b";
	}
	else if(missionVariables.mymission == "STAGE1b")
	{
		mission.runScreen({messageKey:"mymission_message1c", choicesKey:"mymission_continue"}, this.missionScreens);
		missionVariables.mymission = "STAGE1c";
	}
	else if(missionVariables.mymission == "STAGE1c")
	{
		mission.runScreen({messageKey:"mymission_message1d"});
		missionVariables.mymission = "STAGE2";
	}
	else if(missionVariables.mymission == "STAGE2" && system.ID == 150)
	{
		mission.runScreen({messageKey:"mymission_message2a", choicesKey:"mymission_continue"}, this.missionScreens);
		missionVariables.mymission = "STAGE2a";
	}
	else if(missionVariables.mymission == "STAGE2a")
	{
		mission.runScreen({messageKey:"mymission_message2b"});
		missionVariables.mymission = "STAGE3";
	}
}

this.missionScreenOpportunity = function()
{
	this.missionScreens();
}
The general idea is that the script shall display a series of four consecutive mission screens in system 100 and later a series of two consecutive mission screens in system 150. Of course other stuff should be done as well, but I want to keep the example simple.

Another question: do I actually need to define a choices-key with one choice only for this type of consecutive mission screens (as it was necessary in the old model), or could I alternatively use the call back function without a choicesKey?

Posted: Thu Jul 22, 2010 10:28 am
by Arexack_Heretic
Good question!
That reminds me that I should add a "PRESS SPACE COMMANDER" choice to my screens....or should I? :?

Posted: Thu Jul 22, 2010 10:31 am
by Thargoid
That would result in each of the first stage mission screen being displayed in turn, one after the other presuming the conditions for the first display are true. The choices key will be ignored, as you're not actually checking it for anything. If you just want to press space between mission screens then you don't need to set the choice key.

Basically after displaying the first one (message1a) the mV will be set to STAGE1a and the function called again. It will then filter down through the if statements and display message1b, set the mV to STAGE1c and so on until it gets to the bottom of the stage 1 section of the list.

At this point the mV would be set to STAGE2, but as the system.ID check would be false it would stop until you flew to system #150, at which the sequence would repeat but for stage2 & 3 this time.

Remember that this.missionScreenOpportunity will fire not just at first docking, but also directly after the end of the display of a mission screen (to allow for a queue of such screens). Hence if the triggering condition is not set in the right way, it is perfectly possible to have the same screen re-appear once it is closed (if the conditions are still true for it) - which will basically lock the player in the screen unless they jump out of the screen by pressing F1 to force a relaunch. Your code is OK for that though as both sequences will end in mV settings that don't trigger any further mission screens.

What you may be better doing is using a second function for all of the callbacks, equivalent to how it used to be done when mission.choice was set and use that to ensure that the missionScreenOpportunity is not triggered when it shouldn't be. That is the most direct way to port the old method to the new one, although it's less optimal in usage.

Posted: Thu Jul 22, 2010 10:35 am
by Thargoid
Arexack_Heretic wrote:
Good question!
That reminds me that I should add a "PRESS SPACE COMMANDER" choice to my screens....or should I? :?
No, that's automagically added if no choice selection is offered.

Re: mission.runScreen and callback

Posted: Thu Jul 22, 2010 10:39 am
by Kaks
ChoicesKey is only needed if you want the player to actually make choices.
If you're not interested in choices, don't add that option, just call the callback function.

It won't destroy Oolite, afaik, but I'd make sure to change mymission before actually callling runScreen:

Code: Select all

this.missionScreens = function()
{
	if(!missionVariables.mymission && system.ID == 100)
	{
		missionVariables.mymission = "STAGE1a";
		mission.runScreen({messageKey:"mymission_message1a"}, this.missionScreens);
	}
	else if(missionVariables.mymission == "STAGE1a")
	{
		missionVariables.mymission = "STAGE1b";
		mission.runScreen({messageKey:"mymission_message1b"}, this.missionScreens);
	}
	else if(missionVariables.mymission == "STAGE1b")
	{
		missionVariables.mymission = "STAGE1c";
		mission.runScreen({messageKey:"mymission_message1c"}, this.missionScreens);
	}
	else if(missionVariables.mymission == "STAGE1c")
	{
		missionVariables.mymission = "STAGE2";
		mission.runScreen({messageKey:"mymission_message1d"});
	}
	else if(missionVariables.mymission == "STAGE2" && system.ID == 150)
	{
		missionVariables.mymission = "STAGE2a";
		mission.runScreen({messageKey:"mymission_message2a"}, this.missionScreens);
	}
	else if(missionVariables.mymission == "STAGE2a")
	{
		missionVariables.mymission = "STAGE3";
		mission.runScreen({messageKey:"mymission_message2b"});
	}
}

this.missionScreenOpportunity = function()
{
	this.missionScreens();
}
By the way, you should be able to enter this.missionScreens in its entirety in the javascript console, you can then type

Code: Select all

		missionVariables.mymission = "STAGE1a";
		mission.runScreen({messageKey:"mymission_message1a"}, this.missionScreens);
to get the ball rolling... :)

Posted: Thu Jul 22, 2010 10:43 am
by Arexack_Heretic
I think the elseif will only run if all conditions above are not met, so the planetX condition will actually be negative for those below?
Better to nest the conditions:

Code: Select all

if at planetX,
  if undefined > stage1
  else if stage1 > stage1a
  else if stage1a > stage1b
etc
els if at planet Y,
  if stage1complete >stage2
  elseif stage2 >stage2a
etc.

Posted: Thu Jul 22, 2010 10:58 am
by Kaks
Don't quite know which computer language you're using there.

and I've no idea which ones are the 'ones below'.

If you give me an example in javascript, and define 'ones below' with some clarity, I'll be able to tell you! :)


What you used there bears some similarity with python, but we're not making OXPs in python, are we?

Posted: Thu Jul 22, 2010 11:17 am
by Arexack_Heretic
*sigh* pedantic anti-pseudocode-ist :p

with below in a nested structure I mean deeper inside the 'nest', the conditions for stage are 'below' the condition for planet location.

Code: Select all

if (system.ID === 100)
{
  if (!missionVariables.Boo) 
  {missionVariables.Boo = "GO"};
  else if(missionVariables.Boo === "GO")
  {//doyourstuff};
  ...
  else if(missionVariables.Boo === "ALMOST_DONE")
  {missionVariables.Boo = "SYS100done"; //dostuff};
}
else if (system.ID === 50)
{
  if(missionVariables.Boo === "SYS100done")
  {//doyourstuff};
  etc.
}
edit: I misunderstood the 'else if' function a bit there, just woke up. never mind.
Still, nesting does provide a safety against the player F1-ing at the first screen and encountering the missionscreens at a different location.

edit2: yes, most of my meager skillz derive from messing with python, don't tell me that nesting is not allowed in JS!?
edit3: uh oh, does JS evaluate code line by line as python does? I'll have a problem if not...

Re: mission.runScreen and callback

Posted: Thu Jul 22, 2010 11:24 am
by Eric Walch
Kaks wrote:
By the way, you should be able to enter this.missionScreens in its entirety in the javascript console, you can then type

Code: Select all

		missionVariables.mymission = "STAGE1a";
		mission.runScreen({messageKey:"mymission_message1a"}, this.missionScreens);
to get the ball rolling... :)
Should work, copy the whole content of that function and paste it in the console. The 1.74 console supports now such long, multiline code.

Posted: Thu Jul 22, 2010 3:31 pm
by Commander McLane
Thanks, that's all I wanted to know. :D (Oh, and I have been doing multi-line inputs into the console for a long time, I'd say even pre-1.73.)

Posted: Thu Jul 22, 2010 3:37 pm
by Thargoid
One other thought, you could perhaps simplify your code a bit by using the switch command, with a couple of if checks for system.ID in the cases for STAGE1 and STAGE2. Could be neater, simpler and faster executing than the nested if's.

Posted: Thu Jul 22, 2010 3:41 pm
by Commander McLane
Thargoid wrote:
One other thought, you could perhaps simplify your code a bit by using the switch command, with a couple of if checks for system.ID in the cases for STAGE1 and STAGE2. Could be neater, simpler and faster executing than the nested if's.
Ah! Yes, I indeed wanted to adopt this method, since I saw it in your scripts. Thanks for reminding me. :D

Posted: Thu Jul 22, 2010 4:11 pm
by docwild
Just as an aside, javascript supports the ?: operator too which goes:

Code: Select all

boolean condition ? do this if true : do this if false ;
so...

Code: Select all

missionVariables.Boo ? dothis() : dothat() ;
Which is handy for when you're nesting simple conditionals.