Difference between revisions of "OXP mission offering"

From Elite Wiki
m (Location for offering)
(How mission offering should be done with JS)
(24 intermediate revisions by 3 users not shown)
Line 1: Line 1:
= How mission offering should be done =
+
= How mission offering should be done with Legacy script =
  
  
Line 11: Line 11:
 
   conditions = ("status_string equal STATUS_DOCKED", "dockedStationName_string equal Your Station Name")
 
   conditions = ("status_string equal STATUS_DOCKED", "dockedStationName_string equal Your Station Name")
  
Here is " Your Station Name" the name of the station as defined in shipyard.plist under "name". You could skip the first test for being docked as dockedStationName_string only has the stationname if you are docked.
+
Here is " Your Station Name" the name of the station as defined in [[shipyard.plist]] under "name". You could skip the first test for being docked as dockedStationName_string only has the stationname if you are docked.
  
 
== Conditions for offering ==
 
== Conditions for offering ==
Line 17: Line 17:
 
Before placing text also check if the missionscreen is not in use. (See the documents in the downloadeble package below for more info)
 
Before placing text also check if the missionscreen is not in use. (See the documents in the downloadeble package below for more info)
  
For Oollite versions till 1.69 that do not know the "notequal" condition or for compatibillity with those versions use:
+
For Oollite versions up to 1.68 that do not know the "notequal" condition or for compatibillity with those versions use:
  
  conditions = (gui_screen_string oneof GUI_SCREEN_STATUS, GUI_SCREEN_EQUIP_SHIP, GUI_SCREEN_MARKET, GUI_SCREEN_SHORT_RANGE_CHART).
+
  conditions = (gui_screen_string oneof GUI_SCREEN_MAIN, GUI_SCREEN_STATUS).
  
You could also explicit check for the mission screen and put your whole action in the else line.
+
At the end of the docking procedure the script runs immediately for one time with gui_screen_string = GUI_SCREEN_MAIN but already with status_string = STATUD_DOCKED. All following moments the script runs with as gui_screen_string the screen you are looking at. When you do nothing further this will be the  GUI_SCREEN_STATUS .  (= F5) You could also explicit check for the mission screen and put your whole action in the else line.
  
  conditions = (gui_screen_string equal GUI_SCREEN_MISSION).
+
  conditions = (gui_screen_string equal GUI_SCREEN_MISSION); do = (); else = (your stuff);
  
But this involves an extra nested condition, so most people will probably prefer the first method. (Versions 1.7 and newer will have a not-equal option when programming in the new introduced javascript)
+
But this involves an extra nested condition, so most people will probably prefer the first method. Versions 1.69 and newer will have a not-equal option.
 +
 
 +
conditions = (gui_screen_string notequal GUI_SCREEN_MISSION).
  
 
== Setting up the message ==
 
== Setting up the message ==
  
In the do part you must set a mission image. If you have no image set it to "none", otherwise you can get an image from a previous offer.
+
In the do part you must set a mission image. If you have no image set it to "none", otherwise you can get an image from a previous offer. Be aware that some computers are case sensitive so use the same casing as the actual picture. The pictures are placed in a folder called "images", inside the OXP.
  
 
  setMissionImage: none
 
  setMissionImage: none
 +
        or
 +
setMissionImage: my_pict.png
  
Than set the output to the MissionScreen with
+
Then set the output to the MissionScreen with
  
 
  setGuiToMissionScreen
 
  setGuiToMissionScreen
  
This command creates an empty missionscreen and any text after this will go to this missionscreen. The text is than placed with:
+
This command creates an empty missionscreen and any text after this will go to this missionscreen. The text is then placed with:
  
 
  addMissionText: entryname
 
  addMissionText: entryname
  
"Entryname" is a key value in the file missiontext.plist  If you use a second addMissionText commands the text is added below the previous. If you add to much text it runs off the screen and is lost. So make sure you don't use to much text.
+
"Entryname" is a key value in the file [[missiontext.plist]] If you use a second addMissionText commands the text is added below the previous. If you add to much text it runs off the screen and is lost. So make sure you don't use to much text.
 
The system automatic puts a "hit space to continue" message at the bottom. On hitting this space key the MissionScreen will clear and the StatusScreen is put up. There exists also a command to put up the StatusScreen but you don't need that with offering.
 
The system automatic puts a "hit space to continue" message at the bottom. On hitting this space key the MissionScreen will clear and the StatusScreen is put up. There exists also a command to put up the StatusScreen but you don't need that with offering.
 +
 +
== Offering with Choices ==
 +
 
When you in addition use the command:  
 
When you in addition use the command:  
  
 
  setMissionChoices: entryname
 
  setMissionChoices: entryname
  
the "hit space to continue" message is replaced by your choices. Your choices is in this case a key entry in the missiontext.plist file with as contents an array. On the bottom of your screen you see the contents of the array and if you select one the variable "missionChoice_string" contains the name of the chosen entry-string.
+
the "hit space to continue" message is replaced by your choices. Your choices is in this case a key entry in the [[missiontext.plist]] file with as contents an array. On the bottom of your screen you see the contents of the array and if you select one, the variable "missionChoice_string" contains the name of the chosen entry-string. When you are offering choices first make clear that:
 
 
== Offering with Choices ==
 
 
 
If you are offering choices first make clear that:
 
  
 
  conditions = ("missionChoice_string undefined")
 
  conditions = ("missionChoice_string undefined")
Line 59: Line 62:
 
  conditions = ("missionChoice_string equal Your String")
 
  conditions = ("missionChoice_string equal Your String")
  
And make sure that on all choices that the player makes the the choice is reseted: "resetMissionChoice"
+
And make sure that on all possible choices that the player can make, the choice is reseted: "resetMissionChoice"
  
Some missions reset the choices just before the offering to make it clear, but in that case it is possible that the choices of another mission that is offered on the same station is reset before it can act on the choices. If you want to play save, just reset the choices on launching so they are clear on the next docking. Also use distinctive text for "Your String" and not just "YES" or "NO" as other badly written OXP could react on those often used responses.
+
Some excisting missions reset the choices just before the offering itself, but in that case it is possible that the choices of another active mission that is offered on the same station is reset before it can act on the choices. If you want to play save, just reset the choices on launching so they are clear on the next docking. Also use distinctive text for "Your String" and not just "YES" or "NO" as other badly written OXP could react on those often used responses.
  
 
== Setting up the mission desciption ==
 
== Setting up the mission desciption ==
  
If you start a mission it is wise to add a short description for later reference by the player with:
+
If you start a mission it is wise to add a short description and or a location for later reference by the player with:
  
 
  setMissionDescription: entryname
 
  setMissionDescription: entryname
 +
addMissionDestination: planet_number
 +
 +
Entryname is a key value in the [[missiontext.plist]] file. This must be a short description as long text won't wrap around but runs off the screen at the right side. For planet_numbet you ca use a plain number between 0 and 255 or a variable between square brackets. The destination is visible as a red cross in the galactic chart. If the mission is over you clear this description and the location with:
 +
 +
clearMissionDescription
 +
removeMissionDestination: planet_number
 +
 +
Notice that you do not need to specify the mission name in setting and clearing the missionDescription. The system automatic uses the name of the missionscript that uses this command to store the description. This also means that you can not use these two commands outside the missionscript like inside a death_action of a ship.
 +
The best lessons are taken by examining existing scripts.  You can download more info and a test OXP here:  [[Media:MOP-Test_1.1.zip|Mission Offering Protocol]]
 +
 +
= How mission offering should be done with JS =
 +
 +
Starting with Oolite 1.74 we have a new method for displaying mission screens, one that minimises the mission screen conflicts that have afflicted scripts for quite some time: [[Oolite_JavaScript_Reference:_Mission#runScreen|mission.runScreen]], please refer to that part of the documentation when writing new missions.
 +
 +
 +
 +
The method described below is only needed with Oolite 1.73 or older, and will not work in future versions of Oolite: it was fairly complex to use, and due to its limitations, conflicts between different OXPs were always likely. The code samples below are offered as documentation of how the system worked in practice in order to understand how older javascript code worked, when updating it to more recent versions of Oolite.
 +
 +
'''N.B.''' From the next paragraph on, whenever javascript is mentioned, that refers to older javascript methods & practices.
 +
 +
 +
For javascript all the legacy scripts considerations apply.
 +
You need make sure you don't set up a message when there is already a mission screen or a report screen displayed. Also you shouldn't set up your mission screen when there are valid mission choices that don't belong to your script. With JS every oxp can react to a handler and when your handler code is triggered you must verify no other oxp has already reacted on that handler before you.
 +
 +
== script example for mission offering on JS ==
  
This must be a short description as long text won't wrap around but runs off the screen at the right side. If the mission is over you clear this description with:
+
Below is an example script for clash free mission offering. It should work with Oolite version 1.73 and newer.
  
  clearMissionDescription.
+
  this.name        = "demo";
 +
this.author      = "eric walch";
 +
this.copyright  = "© 2008 eric walch.";
 +
this.description = "Empty script structure that handles population and mission offering";
 +
this.version    = "0.0";
 +
 +
// N.B. Works only with 1.73+.
 +
 +
// startUp only runs once at startup before the demoscreen shows up.
 +
this.startUp = function()
 +
{
 +
    this.reset(); // this.reset() fires not for a cmdr Jameson. When needed for a Jameson, call it here.
 +
}
 +
 +
// this runs after loading in a saved game. missionVariables are read in at this point.
 +
this.reset = function()
 +
{
 +
    this.mustPopulate = true;
 +
    /* used for adding ships after a first launch.
 +
    This way you don't have to time consuming check every launch if a ship was already there.
 +
    */
 +
}
 +
 +
this.shipWillDockWithStation = function(station)
 +
{
 +
    if(missionVariables.offering) missionVariables.offering = null;
 +
    /* When using the missionVariables.offering variable in script you need this function to clear the
 +
    variable in case the player launched from within the missionscreen without making choices. By clearing
 +
    this, most scripts that are created acording this layout will automatic do the offer again.
 +
    */
 +
   
 +
    /*
 +
    This is also the place to add your messages to the arrival report with:
 +
    if(condition) player.addMessageToArrivalReport(expandDescription("Your message"));
 +
    */
 +
}
 +
 +
this.shipDockedWithStation = function(station)
 +
{
 +
        // an other eventhandler could have launched the ship already when it fires before this script.
 +
        if(player.ship.docked) this.missionOffers();
 +
}
 +
 +
this.missionScreenEnded = this.missionChoiceWasReset = this.reportScreenEnded = function()
 +
{
 +
    if(!player.ship.docked) return;
 +
    if(mission.choice) this.choiceEvaluation();
 +
    this.missionOffers();
 +
}
 +
 +
this.missionOffers = function()
 +
{
 +
    if (guiScreen == "GUI_SCREEN_MISSION" || guiScreen == "GUI_SCREEN_REPORT") return;
 +
// there will be a "missionScreenEnded" or "reportScreenEnded" in future to react uppon.
 +
    if (missionVariables.offering || (mission.choice && mission.choice != "")) return;
 +
// when "missionVariables.offering" or "mission.choice" are present it means that an other script claimed the mission_screen.
 +
    if (player.ship.dockedStation.isMainStation)
 +
    {
 +
        //  Your stuff for the main station
 +
        /*
 +
        Missionscreens are set up using the command:
 +
        mission.runMissionScreen("mission_text", "backgroundscreen.png", "choices_text", "ship_role", "music")
 +
        You can transfer 5 variables at once that formerly had to set or cleared each seperately.
 +
        When not needed you can transfer a null. nulls at the end can be omitted, in between must be used.
 +
       
 +
        When you do an offer you must set a variable to tell an offer is made. It is not safe to use the main mission
 +
        variable for this. Use missionVariables.offering for this. At the same time you know an other script is busy
 +
        offering when this variable is set. In case the player launched from within a missionscreen without making an
 +
        offer, the missionVariables.offering should be reset by script. Please do it on docking and not launching.
 +
        That way other oxp's that might need the value get an last change on using its content.
 +
        */
 +
    }
 +
    else if (player.ship.dockedStation.shipDescription == "My Station")
 +
    {
 +
        // Your stuff for a special named station
 +
    }
 +
}
 +
 +
this.choiceEvaluation = function()
 +
{
 +
    if (missionVariables.offering == "MY_OFFER")
 +
    {
 +
        /* It is saver to not use a simple YES, but use a YES_MISSION. This just for the case there is
 +
        a conflict with an other bad written oxp that also has a simple YES as answer. */
 +
        if(mission.choice == "YES_MISSION")
 +
        {
 +
            // put your code here.
 +
            missionVariables.offering = null;
 +
            mission.choice = null;
 +
            /* resetting the mission.choice triggers the "missionChoiceWasReset" handler.
 +
            therefor this must always happen after other variables are reset to prefent that the script
 +
            tries to run again.  */
 +
        }
 +
        else
 +
        {
 +
            if (mission.choice == "NO_MISSION")
 +
            {
 +
                // put your code here
 +
                missionVariables.offering = null;
 +
                mission.choice = null;
 +
            } else {missionVariables.offering = null} // one should never get here.
 +
        }
 +
    }
 +
}
 +
 +
this.setUpShips = function()
 +
{
 +
    // Put here your code that populates the system.
 +
}
 +
 +
this.shipExitedWitchspace = function()
 +
{
 +
    if(system.isInterstellarSpace) return;
 +
    this.setUpShips();
 +
    // Put here your code else than setting ships up.
 +
}
 +
 +
this.playerEnteredNewGalaxy = function(Galaxy)
 +
{
 +
    // This handler will be rarely needed.
 +
}
 +
 +
this.shipLaunchedFromStation = function()
 +
{
 +
  /* The first launch after a restart the system must be populated after a launch.
 +
  The other times it happens after a WitchspaceJump */
 +
  if(this.mustPopulate) {this.setUpShips(); this.mustPopulate = false}
 +
    // put here your other code.
 +
}
  
The best lessons are done by examining existing scripts.  You can download more info and a test OXP here:  [[Media:MOP-Test1.0.zip|Mission Offering Protocol]]
+
[[Category:Oolite scripting]]

Revision as of 12:03, 21 July 2010

How mission offering should be done with Legacy script

Location for offering

Missions should only be offered while being docked. To prevent offering on special stations make sure you are docked at the main station:

conditions = ("dockedAtMainStation_bool equal YES")

Only if you are offering something on a special station you can check for just being docked, but then you should also check the station name.

 conditions = ("status_string equal STATUS_DOCKED", "dockedStationName_string equal Your Station Name")

Here is " Your Station Name" the name of the station as defined in shipyard.plist under "name". You could skip the first test for being docked as dockedStationName_string only has the stationname if you are docked.

Conditions for offering

Before placing text also check if the missionscreen is not in use. (See the documents in the downloadeble package below for more info)

For Oollite versions up to 1.68 that do not know the "notequal" condition or for compatibillity with those versions use:

conditions = (gui_screen_string oneof GUI_SCREEN_MAIN, GUI_SCREEN_STATUS).

At the end of the docking procedure the script runs immediately for one time with gui_screen_string = GUI_SCREEN_MAIN but already with status_string = STATUD_DOCKED. All following moments the script runs with as gui_screen_string the screen you are looking at. When you do nothing further this will be the GUI_SCREEN_STATUS . (= F5) You could also explicit check for the mission screen and put your whole action in the else line.

conditions = (gui_screen_string equal GUI_SCREEN_MISSION); do = (); else = (your stuff);

But this involves an extra nested condition, so most people will probably prefer the first method. Versions 1.69 and newer will have a not-equal option.

conditions = (gui_screen_string notequal GUI_SCREEN_MISSION).

Setting up the message

In the do part you must set a mission image. If you have no image set it to "none", otherwise you can get an image from a previous offer. Be aware that some computers are case sensitive so use the same casing as the actual picture. The pictures are placed in a folder called "images", inside the OXP.

setMissionImage: none
       or
setMissionImage: my_pict.png

Then set the output to the MissionScreen with

setGuiToMissionScreen

This command creates an empty missionscreen and any text after this will go to this missionscreen. The text is then placed with:

addMissionText: entryname

"Entryname" is a key value in the file missiontext.plist If you use a second addMissionText commands the text is added below the previous. If you add to much text it runs off the screen and is lost. So make sure you don't use to much text. The system automatic puts a "hit space to continue" message at the bottom. On hitting this space key the MissionScreen will clear and the StatusScreen is put up. There exists also a command to put up the StatusScreen but you don't need that with offering.

Offering with Choices

When you in addition use the command:

setMissionChoices: entryname

the "hit space to continue" message is replaced by your choices. Your choices is in this case a key entry in the missiontext.plist file with as contents an array. On the bottom of your screen you see the contents of the array and if you select one, the variable "missionChoice_string" contains the name of the chosen entry-string. When you are offering choices first make clear that:

conditions = ("missionChoice_string undefined")

And in the following conditions:

conditions = ("missionChoice_string equal Your String")

And make sure that on all possible choices that the player can make, the choice is reseted: "resetMissionChoice"

Some excisting missions reset the choices just before the offering itself, but in that case it is possible that the choices of another active mission that is offered on the same station is reset before it can act on the choices. If you want to play save, just reset the choices on launching so they are clear on the next docking. Also use distinctive text for "Your String" and not just "YES" or "NO" as other badly written OXP could react on those often used responses.

Setting up the mission desciption

If you start a mission it is wise to add a short description and or a location for later reference by the player with:

setMissionDescription: entryname
addMissionDestination: planet_number

Entryname is a key value in the missiontext.plist file. This must be a short description as long text won't wrap around but runs off the screen at the right side. For planet_numbet you ca use a plain number between 0 and 255 or a variable between square brackets. The destination is visible as a red cross in the galactic chart. If the mission is over you clear this description and the location with:

clearMissionDescription
removeMissionDestination: planet_number

Notice that you do not need to specify the mission name in setting and clearing the missionDescription. The system automatic uses the name of the missionscript that uses this command to store the description. This also means that you can not use these two commands outside the missionscript like inside a death_action of a ship. The best lessons are taken by examining existing scripts. You can download more info and a test OXP here: Mission Offering Protocol

How mission offering should be done with JS

Starting with Oolite 1.74 we have a new method for displaying mission screens, one that minimises the mission screen conflicts that have afflicted scripts for quite some time: mission.runScreen, please refer to that part of the documentation when writing new missions.


The method described below is only needed with Oolite 1.73 or older, and will not work in future versions of Oolite: it was fairly complex to use, and due to its limitations, conflicts between different OXPs were always likely. The code samples below are offered as documentation of how the system worked in practice in order to understand how older javascript code worked, when updating it to more recent versions of Oolite.

N.B. From the next paragraph on, whenever javascript is mentioned, that refers to older javascript methods & practices.


For javascript all the legacy scripts considerations apply. You need make sure you don't set up a message when there is already a mission screen or a report screen displayed. Also you shouldn't set up your mission screen when there are valid mission choices that don't belong to your script. With JS every oxp can react to a handler and when your handler code is triggered you must verify no other oxp has already reacted on that handler before you.

script example for mission offering on JS

Below is an example script for clash free mission offering. It should work with Oolite version 1.73 and newer.

this.name        = "demo"; 
this.author      = "eric walch";
this.copyright   = "© 2008 eric walch.";
this.description = "Empty script structure that handles population and mission offering"; 
this.version     = "0.0"; 

// N.B. Works only with 1.73+.

// startUp only runs once at startup before the demoscreen shows up.
this.startUp = function()
{
    this.reset(); // this.reset() fires not for a cmdr Jameson. When needed for a Jameson, call it here.
}

// this runs after loading in a saved game. missionVariables are read in at this point.
this.reset = function()
{
   this.mustPopulate = true;
   /* used for adding ships after a first launch.
   This way you don't have to time consuming check every launch if a ship was already there.
   */
}

this.shipWillDockWithStation = function(station)
{
   if(missionVariables.offering) missionVariables.offering = null;
   /* When using the missionVariables.offering variable in script you need this function to clear the
   variable in case the player launched from within the missionscreen without making choices. By clearing
   this, most scripts that are created acording this layout will automatic do the offer again.
   */
   
   /*
   This is also the place to add your messages to the arrival report with:
   if(condition) player.addMessageToArrivalReport(expandDescription("Your message"));
   */
}

this.shipDockedWithStation = function(station)
{
       // an other eventhandler could have launched the ship already when it fires before this script.
       if(player.ship.docked) this.missionOffers();
}

this.missionScreenEnded = this.missionChoiceWasReset = this.reportScreenEnded = function()
{
   if(!player.ship.docked) return;
   if(mission.choice) this.choiceEvaluation();
   this.missionOffers();
}

this.missionOffers = function()
{
   if (guiScreen == "GUI_SCREEN_MISSION" || guiScreen == "GUI_SCREEN_REPORT") return; 
	// there will be a "missionScreenEnded" or "reportScreenEnded" in future to react uppon.
   if (missionVariables.offering || (mission.choice && mission.choice != "")) return;
	// when "missionVariables.offering" or "mission.choice" are present it means that an other script claimed the mission_screen.
   if (player.ship.dockedStation.isMainStation)
   {
       //  Your stuff for the main station
       /*
       Missionscreens are set up using the command:
       mission.runMissionScreen("mission_text", "backgroundscreen.png", "choices_text", "ship_role", "music")
       You can transfer 5 variables at once that formerly had to set or cleared each seperately.
       When not needed you can transfer a null. nulls at the end can be omitted, in between must be used.
       
       When you do an offer you must set a variable to tell an offer is made. It is not safe to use the main mission
       variable for this. Use missionVariables.offering for this. At the same time you know an other script is busy
       offering when this variable is set. In case the player launched from within a missionscreen without making an
       offer, the missionVariables.offering should be reset by script. Please do it on docking and not launching.
       That way other oxp's that might need the value get an last change on using its content.
       */
   }
   else if (player.ship.dockedStation.shipDescription == "My Station")
   {
       // Your stuff for a special named station
   }
}

this.choiceEvaluation = function()
{
   if (missionVariables.offering == "MY_OFFER")
   {
        /* It is saver to not use a simple YES, but use a YES_MISSION. This just for the case there is
        a conflict with an other bad written oxp that also has a simple YES as answer. */
       if(mission.choice == "YES_MISSION")
       {
           // put your code here. 
           missionVariables.offering = null;
           mission.choice = null;
           /* resetting the mission.choice triggers the "missionChoiceWasReset" handler. 
           therefor this must always happen after other variables are reset to prefent that the script
           tries to run again.  */
       }
       else
       {
           if (mission.choice == "NO_MISSION")
           {
               // put your code here
               missionVariables.offering = null;
               mission.choice = null;
           } else {missionVariables.offering = null} // one should never get here.
       }
   }
}

this.setUpShips = function()
{
   // Put here your code that populates the system.
}

this.shipExitedWitchspace = function()
{
   if(system.isInterstellarSpace) return;
   this.setUpShips();
   // Put here your code else than setting ships up.
}

this.playerEnteredNewGalaxy = function(Galaxy)
{
   // This handler will be rarely needed.
}

this.shipLaunchedFromStation = function()
{
  /* The first launch after a restart the system must be populated after a launch.
  The other times it happens after a WitchspaceJump */
  if(this.mustPopulate) {this.setUpShips(); this.mustPopulate = false}
   // put here your other code.
}