Back to Index Page generated: Dec 20, 2024, 7:22:09 AM

Expansion Escort Contracts

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Start your Escort Contracts in the interfaces (F4) screen after you bought an ITHA license. Start your Escort Contracts in the interfaces (F4) screen after you bought an ITHA license.
Identifier oolite.oxp.CaptMurphy.EscortContracts oolite.oxp.CaptMurphy.EscortContracts
Title Escort Contracts Escort Contracts
Category Missions Missions
Author Capt. Murphy, Keeper, Norby, ZygoUgo, Fritz G. Capt. Murphy, Keeper, Norby, ZygoUgo, Fritz G.
Version 1.7.1 1.7.1
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/Escort_Contracts_OXP n/a
Download URL https://wiki.alioth.net/img_auth.php/5/58/Escort_Contracts_1.7.1.oxz n/a
License CC BY-NC-SA 3 CC BY-NC-SA 3
File Size n/a
Upload date 1610873302

Documentation

Also read http://wiki.alioth.net/index.php/Escort%20Contracts

Captain Murphy's Original readme.rtf

{\rtf1\ansi\ansicpg1252\deff0\deflang2057\deflangfe2057{\fonttbl{\f0\fswiss\fprq2\fcharset0 Arial;}}
{\colortbl ;\red0\green0\blue255;}
{\*\generator Msftedit 5.41.21.2510;}\viewkind4\uc1\pard\nowidctlpar\f0\fs20 Escort Contracts version 1.5.6 (07/05/12)\par
By Capt Murphy 2011,2012\par
Licence: CC BY-NC-SA 3.0 : Please visit {\field{\*\fldinst{HYPERLINK "http://creativecommons.org/licenses/by-nc-sa/3.0/"}}{\fldrslt{\ul\cf1 http://creativecommons.org/licenses/by-nc-sa/3.0/}}}\f0\fs20  for more info.\par
Requires Oolite 1.76\par
\par
Overview: Allow players to enter into short-term escort contracts with NPC lone wolf traders.\par
\par
Description:\par
\par
The Independent Traders and Hauliers Association have reacted to demand from lone wolf NPC Traders and introduced a system to allow them to enter into short-term escort contracts with vetted and rated freelance escort pilots.\par
\par
Players who wish want to access these contracts must purchase an I.T.H.A Independent Escort License available to players with a Clean legal status at systems with a Tech Level of 3 or above and not Anarchies, Feudal States or Multi-Governments.\par
\par
Once purchased players whose legal rating is still Clean may be offered short term escort contracts on docking at a system\rquote s main station. The chance of being offered contracts is based on the player\rquote s reputation as an escort. The fees offered are also affected by the player\rquote s reputation as an escort and government type of the target system. Target systems are always within 1 jump of the starting system. Reputation is increased when a contract is successfully fulfilled and reduced when a contract is failed.\par
\par
Once a contract is agreed the player has 3 game hours to launch from the station or the contract is failed (be careful about buying equipment \endash  it takes up time). The NPC trader (or 'mother') will be waiting in the station aegis and will prompt the player to approach as they prepare to jump to the target system. The player can follow them through their wormhole or alternatively jump themselves to the target system. Failure to arrive at the target system by the point the wormhole expires ends the contract.\par
\par
Once in the target system the player\rquote s task is to escort the mother to the system's main station. Mothers will issue various prompts to players in flight, will request assistance if attacked, and will assist the player if the player is attacked. The contract is fulfilled once the mother has entered the aegis of the target system's main station, unless there are hostile craft in the vicinity in which case the player must stand by to protect the mother until it has safely docked.\par
\par
Game Balance and Playability:\par
\par
In order to avoid these contracts being long and rather dull in between hostile encounters, this OXP gives the mother the ability to 'synchronise jump drives' with the player. The mother has been given scan_class CLASS_ROCK (but with custom scanner colours) so will not mass-lock the player. In flight if there is nothing else to mass-lock the player the mother will prompt the player to approach to between 500m - 2500m and to match heading.  If the player does so the mother will synchronise jump drives with the player and both mother and player will travel on the mother\rquote s heading at torus speeds, until the player or mother is mass-locked again.\par
\par
If the player is mass-locked but with no hostile contacts in the vicinity the mother may after a short delay decide to try and use injectors to get out of the mass-locked situation so that the player and mother can take advantage of the mother\rquote s synchronised jump drive capability. This feature will only operate if the player also has working injectors and at least 1 LY of fuel.\par
\par
Mothers will normally be an Anaconda, Boa, Boa Cruiser or Python and all come equipped with ECM, injectors, and fore and aft beam lasers as standard. \par
\par
The shipdata.plist is set up to 'like_ship' to the core versions of these ships (or a retextured version if the core versions have been replaced by a retexture OXP), and if installed any of the versions from retexture OXPs that add variants in addition to core ships. Currently supported are Griff's, DeepSpace, Shady Sungs, Smiv's and Neolite Core.\par
\par
If you have a retexture pack installed that does not replace the core models and want to suppress usage of the core models in this OXP you can edit the shipdata.plist to reduce the roleweighting of the core ships. Instructions are included in the shipdata.plist as comments.\par
\par
The shipdata.plist is also set up to use these OXP ships as potential mothers if installed: \par
From Neolite Companion : Monitor, Python Cruiser, Python ET Special; \par
From Old Ships 2010 : Monitor, Monitor2;\par
From Far Arm Ships : Tanker;\par
From Clippers: Python Clipper, Boa Clipper.\par
From Staer9's Shipset: Monitor, Python Cruiser, Python ET Special.\par
\par
You can use these entries as a template to use ships from your favorite OXP. However be wary of doing this with any OXP ship that normally requires its own shipscript, or is too large to dock at a normal station as the results may be unpredictable.\par
\par
If you have an Advanced Space Compass installed the Mother will be marked with an \lquote M\rquote  beacon code.\par
\par
Changelog:\par
\par
Version 0.9 (24/4/11) - Initial Release.\par
\par
Version 0.9.1 (29/4/11)\par
\par
Fixed a bug in detecting player kills (the mother should pay a bonus for hostile kills made during the contract - was broken in version 0.9). \par
Added code for mother to use injectors to try and escape from mass lock situations after a few minutes.\par
Made criteria for contract success arrival in target system's station aegis rather than mother safely docked to save player time. \par
Replaced shield enhancers with shield boosters as standard equipment for mothers as they were a little too uber. \par
Added code to stop the I.T.H.A. Independent Escort License being damaged in combat. \par
Added an additional world script which displays the players escort reputation on the manifest screen.\par
\par
Version 0.9.2 (29/4/11)\par
\par
Fixed a small bug in calculation and display of contract prices.\par
\par
Version 1.0 (8/5/11)\par
\par
Added a cap to escort reputation.\par
Removed shield boosters as standard equipment for mothers and gave them fore beam lasers instead of military lasers. Mothers now really do need your help to survive an encounter with multiple hostiles.\par
Many improvements to the AI for the mother using injectors feature.\par
Fixed a cosmetic bug in the mission screen timing.\par
Fixed a bug where the mother could still be found waiting to dock after the player had docked, met the mother\rquote s pilot in the bar, got their reward and re-launched.\par
Improvements and bug fixes to the combat AI, mainly relating to mother - player communication.\par
Removed player auto targeting mother\rquote s primary aggressor (potential for confusion if other auto target-locking OXP\rquote s are installed).\par
Adjusted criteria for contract success. If no hostiles in vicinity the contract is fulfilled once the mother is in the station aegis. If there are hostiles in the vicinity when the mother arrives in the aegis the player must not dock until the mother is safely docked to fulfill the contract. The mother will explicitly tell the player this on arrival at the station aegis.\par
Added code so that if repeatedly attacked by the player, the mother will eventually \lquote turn\rquote  and attack the player. The contract is immediately failed at this point.\par
Added code so the timer that checks to see if the player has followed the mother through it\rquote s wormhole kicks as the wormhole closes. Uses same calculation as oolite engine to work out wormhole expiry time depending on the mother ship mass.\par
\par
Version 1.1 (15/6/11)\par
\par
Various improvements to AI following tester reports of occasional anomalous docking and route finding behaviour.\par
Added a pilot and cargo to the mother (thanks to Eric Walch for the tip).\par
Added code to gradually reduce a positive or negative escort reputation over time.\par
Added code so that any fire from a players \lquote Hired Guns\rquote  from the OXP of the same same are treated as Friendly Fire.\par
\par
Version 1.2 (13/7/11)\par
\par
Fixed dockingAI that would not parse correctly on Mac systems.\par
Tweaked and tidied the synchronised jump code. Player must be between 500m-2500m and on approximately same heading as mother before synchronised jump will happen. The minimum distance has been introduced to avoid mother making course changes whilst hopping due to avoiding \lquote collision\rquote  with the player.\par
Added an additional check to avoid anomalous communication from the mother when docked.\par
\par
Version 1.3 (7/8/11)\par
\par
Added code to Mission Screen handling to allow access to Short Range Chart whilst perusing contracts.\par
Added check to avoid contract being offered to post-Nova system.\par
\par
Version 1.3.1 (7/8/11)\par
\par
Fixed missiontext.plist that would not parse correctly on Mac systems.\par
\par
Version 1.4 (19/9/11)\par
\par
Changed synchronised jump mechanism to use ship.velocity and a frame callback to simulate synchronised jump behaviour rather than a \lquote teleport/hop\rquote . Change prompted by reports of original method causing a crash to desktop on some systems (seemingly caused by changing the mother\rquote s ship.position by script), and retained as the effect is more pleasing. Thanks to FleurdeMal for reporting the crash and testing the revised version.\par
\par
If player leaves mission screen to access short range chart and does not return within a fairly short time period there is a chance that the contracts on offer will have changed.\par
\par
Tweaked AI for initial hyperspace jump to reduce incidence of mother trying to jump when jumps are blocked by the station\rquote s mass.\par
\par
Version 1.4.1 (24/9/11)\par
\par
Minor tweaks. Can now access both short range and long range charts and return to mission screens.\par
\par
Tweaked the AI, so mother more likely to use injectors if mass-locked repeatedly by same NPC.\par
\par
Version 1.4.2 (20/11/11)\par
\par
Some tweaks to the mother ship-script to reduce processing overhead.\par
Fixed small bug where player was rewarded extra for a \lquote hostile\rquote  kill even if the kill was an asteroid or boulder.\par
\par
Version 1.5 (28/1/12)\par
\par
Made JS strict mode compatible.\par
Made Save Anywhere OXP compatible.\par
Reduced use of missionVariables.\par
Tweaked calculation of contract rewards - average is 25% lower than previous versions but with significant random variation.\par
Kills are only counted by the mother if the player is within 3 x her scanner range.\par
If mother is 'abandoned' by the player for any length of time there is a chance she will be ambushed by pirates - frequency is higher in more dangerous government types.\par
Added a little randomness to some of the comms messages from the mother.\par
Eliminated unneccesary and irritating repeat comms messages, particulary the 'Jump Drive Synchronised' message.\par
Added a background image to mission screens.\par
Made the code to remove mother ship from ooniverse on contract failure more robust.\par
Found and fixed a few potential bugs if the player does something 'weird' such as not dock to collect the reward at the end of a contract.\par
\par
Version 1.5.1 (05/02/12)\par
\par
Fixed small bug in display of reputation introduced in last release.\par
Reduced frequency of some other comms messages in response to user feedback.\par
Expanded shipdata.plist to use ship variants as mothers from  the 5 main retexture shipsets if available, and several other OXP ships if available.\par
\par
Version 1.5.2 (05/03/12)\par
\par
Tweaked AI to stop mother injecting indefinately in fleeing from attack in starting system.\par
Reduced max speed of escort contracts variant of Boa Clipper.\par
Enabled entries for the addition version of Smiv's v4 shipset.\par
\par
Version 1.5.3 (07/03/12)\par
\par
Fixed small bug where player was rewarded extra for a \lquote hostile\rquote  kill even if the kill isCargo.\par
\par
Version 1.5.4 (21/04/12)\par
\par
Tweak to the chances of contracts being offered. Was in a range of 25% to 100% depending on escort reputation, now a range of 20% to 80%.\par
Set specific spawn position for mother of 19km directly behind main station to avoid issues with bigger stations (e.g the Torus) (thanks to cim for bug report and suggestion for appropriate fix).\par
\par
Version 1.5.5 (05/05/12)\par
\par
Added Staer9 shipset versions of the  Monitor, Python Cruiser, & Python ET Special as potential mother variants if the OXP is installed.\par
Made the Escort License transferable equipment when buying a new ship.\par
\par
Version 1.5.6 (07/05/12)\par
\par
Spawn position set to sphere radius 4km centred on a spot 14km directly behind main station.\par
Mother will hold this position until player approaches and then start moving directly away from station/planet rather than towards witchpoint, whilst preparing to jump.\par
If mother's maxSpeed is greater than 95% of player.ship.maxSpeed she will limit her cruising speed and injector speed to 95% of equivalent player.ship.speeds (apart from in combat).\par
Fixed logic bug with behaviour controlling when to start/stop injecting. Starting was based on player's mass lock status, ending on the mothers. Ending now checks both.\par
Increased frequency of check to stop injecting to every second - previously every 3 seconds.\par
Thanks to lone-wolf and cim for highlighting the issues leading to these tweaks.\par
\par
\par
\par
}

ReadMe 1.6.3.txt

License here:  http://creativecommons.org/licenses/by-nc-sa/3.0/

1.6.3 (fix by phkb, 2015-08-25)

Fixed chart screens so that text is not overlaying it anymore. 
Changed files: missiontext.plist, Escort_Contracts.js

1.6.2 (fix by Keeper, 2015-06-07)

Bug correction when loading a game with an escort mission.

1.6.1 Unofficial update (calling it 1.6.1), to work with Oolite 
      V1.77's interfaces screen (and other updates), by Keeper.

All credit to Captain Murphy for creating the OXP.
New background images by ZygoUgo.

How it works now:

No longer will new contracts be shown when you dock at a station
(though results of an existing mission will be, as usual). Instead,
if a contract is available, you will find an "ITHA Escort Contracts"
entry on the interfaces screen (F4 or 4 while docked at a main station)
showing you how many are available.

When you view the contracts, you still have the option of viewing the
Short Range Chart, but instead of actually going to the real Short
Range Chart GUI, it loads the chart as an overlay, so you have an
immediate ability to accept or decline the contract while looking
at the chart (thanks to cim for the heads-up about this new feature).

That Short Range Chart view also will give you the key information
(trader name, destination, pay, and risk) in a terse form at the top
of the screen, so you won't have to memorize what the contract entailed.
It also will display the economy type, government type, and tech level
of both the current and the destination system, to help you decide
which contract to accept if you're carrying cargo yourself.

All the menu selections have been aligned to the right of the screen,
so that when viewing the chart, the text is out of the way of the
planets and your fuel range. Also, all the menu selection positions
will be maintained. For example, when you "View next contract", the
next page will have "View next contract" pre-selected, so you can page
through offers more easily. Also, the "Agree" option never will be
pre-selected, so you can't accept an offer by mistake. When you
have toggled the chart view on, it will remain on when you view the
next or previous offer.

If you decline the contracts, the offers will stay available up to
a point. The timer that the OXP had before while you were looking
at the chart is still running.

When the timer expires (a random time but usually a few hours),
the contract offers will disappear and no new offers will be made.
That's different than the original OXP. Fact is I never got it to
work right when having it create new offers. Either all the variables
were empty or it accumulated a huge number of new offers. I'm sure
it's possible to get it right, but in most cases you're not going
to be at the station long enough (even doing ship maintenance)
for the offers to go away, so it's not a big deal.

Of course, you also can launch from the station, turn around and re-dock,
hoping to see new offers turn up... It is left to people who actually are
programmers to work out a more robust system of preventing such a "cheat"
way of fishing for better contracts.

You may find offers available when you load a saved game, so you
may not actually have to go somewhere else to continue your escort work.
(I say "continue" because it still will check to make sure that you have
purchased an ITHA licence and that you have a Clean legal status.)

There are now three different success screens, so that it won't mention
getting a bonus if you made no kills during the trip, and it will use
the singular "kill" if you got only one.

The Griff Prototype Boa has been added as a potential mothership.
I only used the one that gets decals from the texture's alpha channel,
as that "alpha" version is the only one whose role normally always is
a trader (the others are or can be pirates), so it looks best that way.

Spelling was corrected, but don't be worried about me being American;
I left in UK spelling of things like "hauliers", "initialising", etc.
I simply corrected misspellings of "its", "independent" (adjective),
"en route" etc., and added commas where needed and replaced commas
with full-stops where needed.

With the initial screen now eliminated, that meant losing one of
ZygoUgo's new background images... or at least it would have if
I hadn't moved it to the "contract accepted" screen!

There is a harmless quirk you may encounter when loading a saved game.
Sometimes, you'll see the IHTA Escort Contracts menu item on the
interfaces screen with "0 available". Most of the time, the menu item
will not appear when there are no contracts available, but I guess it is
possible for you to go to the F4 screen before the menu removal code
has run. It doesn't cause any problems, and the line goes away when you
go to another screen and then back to the F4 screen.

ReadMe 1.7.1.txt

License here:  http://creativecommons.org/licenses/by-nc-sa/3.0/
--------------------------------------------------------------------------
Credit to Captain Murphy for creating the OXP and maintaining the
versions up to 1.5.x, to Norby, Keeper, Chimrod and phkp for the fixes 
in versions 1.6.x, and to ZygoUgo for the background images.
--------------------------------------------------------------------------


Version 1.7.1 (2015-12-04 by Fritz G.) 

This version fixes several bugs (see below), but most changes apply to the 
comms messages. Some of these were repeated much too often, which could 
be very annoying, especially during fighting. This was fixed by adding 
some counters in the code, and some message were shortened a litle bit, 
as well as the ship's name, which is now "[name]'s [ship type]" instead 
of "Trader [name]'s [ship type]".

Most comms messages can now appear in different variants, adding more 
realism in the style of Comms Pack A. This was achieved by using string 
expansion and adding a discriptions.plist.

Another more than minor change is the contract assignment procedure: 
While trying to find the "0 available" bug (see below) I noticed that target
systems had to have a lower government type than the current system. This 
meant that fewer contracts were offered in unsafe systems, and getting a 
contract in anarchies was impossible. 

The new logic simply choses from all systems in jump range with a
government type of 5 or lower. The probability of one of them being
chosen is still higher for unsafe systems. I have moved the probability 
factors and a switch for changing between the old and the new selection 
logic to the begin of Escort_Contracts.js, so that it is easy to adjust 
without having to dig deep into the code.

Because of these rather significant changes, I decided to change the 
version number to 1.7.1. instead of 1.6.4.

What I didn't change is the hectic and somtimes a litte suicidal mother AI. 
The experience of escorting and fighting should be exactly the same as before, 
except that the mother isn't so talkative anymore.  

I tested my changes only with Oolite version 1.82 on Windows 7, but they 
should work with version 1.79 and newer too.


Bugfixes: 
- No NPC escorts for the mother (fix in shipdata.plist) 
- The mission will not break anymore if the player arrives in the target 
  system some seconds before the mother. Was a follow-up bug to some 
  strange behaviour of Oolite itself.  
- "Escort Contracts (0 available)" will not appear anymore on the F4 
  screen, the contract option is not shown at all in this case. 
- After docking, new escort contracts are available immediately. In older 
  versions you had to save and reload the game. (You can still do this 
  repeatedly to "cheat" if the offered contracts don't fit you, but I 
  consider this a minor issue, because the differences in payment are not 
  as big as with parcel or passenger contracts.)
- No new missions will be offered after loading a game including an 
  accepted contract.
- Price calculation now works after flying a mission that was saved and 
  reloaded (was a follow-up bug to the previous one). 
- Upper case letters in background filenames changed to lower case, fixing 
  the missing background issue in the mission success screens. 
- Using the map during contract selection doesn't change the target system  
  anymore.
  
Comms messages:
- Added descriptions.plist with random variations of most messages.  
- Added suppression code to prevent some messages to be repeated over and 
  over again, somtimes only seconds apart. In these cases, the electronic 
  voice coldn't keep up. There is still room for improvement in some cases.
- Some communication messages shortened and "Trader" omitted from 
  displayName for better working with eSpeak. 
- Replaced "jump drive" with "torus drive" consequently to avoid confusion.

Other:  
- Torus synchronisation will need a little more precision in heading 
  alignment, but in practice this doesn't make it more difficult for the 
  player. I changed it because synchronised torus driving looks odd if the 
  headings differ too much. 
- Added beacon_label "Escort Mother" in shipdata.plist 
- The "python-trader" is included in shipdata.plist as a possible mother.
- Adapted ec_mother_dockingAI.plist to dockingAI.plist from Oolite 1.82,
  hoping that this will fix the problem of the docking procedure getting 
  stuck sometimes. The only difference between the two docking AIs is 
  now the AI switched to when being attacked. I still have stuck traders
  in rare cases, mostly when I'm immediately behind them in the docking
  corridor, but it's difficult to reproduce. If a trader is stuck, it can
  be 'reanimated' by approaching very close and activating the collision 
  avoidance behaviour, so a mission can still be finished successfully.
- Script code tidied up (mostly indention and bracket issues). 

Modified or added files (compared to version 1.6.3)
- descriptions.plist (new file)
- ec_mother.js 
- Escort_Contracts.js 
- Escort_Contracts_Rep.js (only a little code cleanup)
- shipdata.plist (escorts fixed, python-trader added)
- equipment.plist (damage_probability=0)
- missiontext.plist (fixed a minor typographic issue)






Equipment

Name Visible Cost [deci-credits] Tech-Level
I.T.H.A Independent Escort License yes 5000 5+

Ships

Name
FA_Tanker_ec_mother
ITHAcoin
PAG_monitor2_ec_mother
PAG_monitor_ec_mother
adck_anaconda_ec_mother
adck_boa-mk2_ec_mother
adck_boa_ec_mother
adck_python_ec_mother
anaconda_ec_mother
boa-mk2_ec_mother
boa_ec_mother
boaclipper_ec_mother
ds-alt-anaconda_ec_mother
ds-alt-boa-mk2_ec_mother
ds-alt-boa_ec_mother
ds-alt-python_ec_mother
griff_anaconda_ec_mother
griff_boa-mk2_ec_mother
griff_boa_ec_mother
griff_boa_prototype_ec_mother
griff_python_ec_mother
monitor_ec_mother
neo_anaconda_ec_mother
neo_boa-mk2_ec_mother
neo_boa_ec_mother
neo_python_ec_mother
python-clipper_ec_mother
python-cruiser_ec_mother
python-trader_ec_mother
python-x_ec_mother
python_ec_mother
smivs_anaconda_ec_mother
smivs_boa-mk2_ec_mother
smivs_boa_ec_mother
smivs_python_ec_mother
staer9_monitor_ec_mother
staer9_python-cruiser_ec_mother
staer9_python-x_ec_mother

Models

This expansion declares no models.

Scripts

Path
Scripts/Escort_Contracts.js
"use strict";
this.name        = "Escort_Contracts"; 
this.version     = "1.7.1";           // 2015-12-04
this.author      = "capt murphy"; 
this.copyright   = "2011 capt murphy";
this.licence     = "CC BY-NC-SA 3.0"; // see http://creativecommons.org/licenses/by-nc-sa/3.0/ for more info.
this.description = "Allows players to enter into short-term escort contracts with NPC traders."; 

//------------------------------------------------------------------------------------------------------------------------------------------
// constants for the contract creation procedure and the random chances involved (calculated in this.$EscortF4Interface()):

this.$ccOldStyle   		= 0;    // If set to 1, contracts are only available to systems with lower government type than the current 
								// system. This was the standard procedure for earlier versions. To accurately recreate the behaviour  
								// of version 1.6.x, $ccFactor must be 0.4 and $ccSystemFactor 0.5. 

this.$ccFactor 			= 0.4;  // Determines by the formula ccFactor * (1+(reputation/20)) how probable it is that contract 
								// creation is started. Standard value is 0.4 for both old and new style creation, this value results 
								// in p = 0.2 for minimum (-10), p = 0.4 for neutral (0) and p = 0.8 for maximum (20) reputation.

this.$ccSystemFactor 	= 0.5; 	// Determines how probable it is that a possible target system actually is chosen for a contract.
								// Using the new style, the probability is calculated by p = ccSystemFactor*(1-govType/10). 
								// Example: ccSystemFactor = 0.5 gives probabilities between 0.15 for type 7 (corporate states)
								// and 0.5 for type 0 (anarchies). 
								// The old style uses p = ccSystemFactor for all planets with government types lower than the
								// current system. 
								// Standard value is 0.5 for both old and new style creation. 

this.$ccMaxGovType		= 5;	// Safest government type that will be considered for contracts, ignored when old style is selected. 
								// Standard value is 5 (confederation).									
								
this.$ccChooseAll 	    = 0;    // increase all contract offering chances to 1 for testing. Should of course be 0 for normal playing! 
								
//------------------------------------------------------------------------------------------------------------------------------------------
								
this.$debugMode = 0;  			// Write debug information to logfile 

this.$resetTargetSystem = 0;    // for storing and resetting the selected target system when using the contract selection map

// initialisation on game startup								
this.startUp = function()
{
	if (!missionVariables.ec_escortrep) // first run of oxp - ec_escortrep should be only persistent variable between save games unless game is saved just after a contract is accepted.
		{this.ec_escortrep = 0;}
	else 
		{this.ec_escortrep = missionVariables.ec_escortrep;} // otherwise load value from save game.

	// Clean up superflous missionVariables from previous versions of OXP.
	// This code was already in version 1.5.6., so it refers to really old versions of this OXP...
	delete missionVariables.ec_contractdifficultydesc;
	delete missionVariables.ec_contractdifficultystars;
	delete missionVariables.ec_escortrepstring;
	delete missionVariables.ec_motherfrustration;
	delete missionVariables.ec_numbertargetsystems;
	delete missionVariables.ec_targetsystemgovernmentname;
	delete missionVariables.ec_targetgovsymbol;
	delete missionVariables.ec_currentsystemeco;
	delete missionVariables.ec_targetecosymbol;
	delete missionVariables.ec_currentsystemname;
	delete missionVariables.ec_currentsystemgov;
	delete missionVariables.ec_currentsystemtech;
	delete missionVariables.ec_currentsystemeco;
	delete missionVariables.ec_targettech;

	if (missionVariables.ec_currentcontract && missionVariables.ec_currentcontract !== "no") 
	{
        // values might be "no" or "YES" if upgrading from previous version of
        // oxp otherwise will be true, "success", or null/false.
        if (missionVariables.ec_currentcontract === "success") 
		{
            // might happen if mother safely enters Aegis, and then player docks at OXP station and uses Save Anywhere.
			this.ec_currentcontract = "success";
        } 
		else 
		{
            // if true or "success" load other contract details from save file.
            this.ec_currentcontract = true;
        } 
		this.ec_contractactualprice = parseInt(missionVariables.ec_dbcontractactualprice);
		this.ec_contractexpiretime 	= missionVariables.ec_contractexpiretime;
		this.ec_killsbonus 			= parseInt(missionVariables.ec_dbkillsbonus);
		this.ec_missionkills 		= parseInt(missionVariables.ec_missionkills);
		this.ec_mothername 			= missionVariables.ec_mothername;
		this.ec_targetsystem 		= missionVariables.ec_targetsystem;
        this.ec_payment 			= 0;
        missionVariables.ec_payment = 0;
		this.ec_runcontractsetup 	= false;   // no contract offers when saved game already includes a contract (fix 1.7.1)
		if (this.$debugMode) log("Escort Contracts: start up, loaded game includes contract with trader " + this.ec_mothername); 	
	} 
	else 
	{
        // otherwise no current contract.
        this.ec_currentcontract = false; 
		delete missionVariables.ec_currentcontract;
		this.ec_runcontractsetup = true;  
		if (this.$debugMode) log("Escort Contracts: start up, loaded game doesn't include a contract"); 	
    } 
 	this.$EscortF4Interface(this);
};

this.playerWillSaveGame = function()
{
	missionVariables.ec_escortrep = this.ec_escortrep; // always save current reputation
	if (this.ec_currentcontract) // only save other contract essential missionVariables if game is saved whilst there is a current contract.
	{
		missionVariables.ec_currentcontract 		= this.ec_currentcontract;
		missionVariables.ec_dbcontractactualprice 	= this.ec_contractactualprice; 
		missionVariables.ec_contractexpiretime 		= this.ec_contractexpiretime;
		missionVariables.ec_dbkillsbonus 			= this.ec_killsbonus;
		missionVariables.ec_missionkills 			= this.ec_missionkills;
		missionVariables.ec_mothername 				= this.ec_mothername;
		missionVariables.ec_targetsystem 			= this.ec_targetsystem;
	}
	else 
	{
		delete missionVariables.ec_currentcontract;
	}
};
	
   
// Event handler driven function checking conditions for showing contract success mission screen or new contract offer screens. 
// Also defines contract failure if a player docks part way through a contract.
this.shipDockedWithStation = function()
{
	delete this.ec_leftScreen; // reset this flag if present.
	if (!this.ec_currentcontract && player.ship.equipmentStatus("EQ_FRAME_SAVE") !== "EQUIPMENT_OK") 
	{
		// if no current contract and dock is not part of Save Anywhere Process then set flag for contract setup.
		this.ec_runcontractsetup = true;
	}
	else if (this.ec_currentcontract === "success" && system.info.systemID === this.ec_targetsystem && player.ship.dockedStation.isMainStation && player.ship.equipmentStatus("EQ_FRAME_SAVE") !== "EQUIPMENT_OK") 
	{
		// if docking after successful contract then process award payment etc, unless dock is part of Save Anywhere process.
		this.ec_payment = this.ec_contractactualprice + (this.ec_missionkills * this.ec_killsbonus);
		player.credits += this.ec_payment;  
		this.ec_escortrep++;               
		if (this.ec_escortrep > 20) 
			{this.ec_escortrep = 20;}
		mission.setInstructionsKey (null, "Escort_Contracts");
		delete this.ec_currentcontract;
		this.ec_runcontractsuccess = true;
		this.ec_runcontractsetup = true; // chance of another contract being offered after mission screen has ended (this was missing before 1.7.1)
		// remove mother from system if she hasn't docked before the player
		if (this.mother && this.mother.isValid) 
			{this.mother.remove(true);}
	}
	else if (this.ec_currentcontract && this.ec_currentcontract !== "success") 
	{
		// docking anywhere whilst contract is ongoing fails contract unless currentcontract is already a "success".
		if (system.info.systemID === this.ec_targetsystem)
		{
			delete this.ec_currentcontract;
			mission.setInstructionsKey (null, "Escort_Contracts");
			this.ec_escortrep -= 2;
			if (this.ec_escortrep < -10)
				{this.ec_escortrep = -10;}
			player.consoleMessage("Escort Contract: You have docked before your mother is safe. Contract terminated.", 10);
			if (this.mother && this.mother.isValid) 
				{this.mother.remove(true);}
		}
		else 
		{
			if (this.mother && this.mother.isValid) 
				{this.mother.remove(true);}
			this.ec_failedtofollowmother();
		}
	}
  	this.$EscortF4Interface(this);
};

// sets up appropriate mission screens for either contract success or new contract offers.
this.missionScreenOpportunity = function()
{
	if (player.ship.docked && player.ship.dockedStation.isMainStation && this.ec_runcontractsuccess) 
	{
		// show screens for contract success if appropriate flag is set.
		delete this.ec_runcontractsuccess;
		this.ec_runcontractsetup = true; // chance of another contract being offered after mission screen has ended.
		missionVariables.ec_mothername 			= this.ec_mothername;
		missionVariables.ec_targetsystemname 	= system.name;
		missionVariables.ec_payment 			= this.ec_payment;
		missionVariables.ec_missionkills 		= this.ec_missionkills;
		if (missionVariables.ec_missionkills === 0)
		{
			mission.runScreen ({screenID:"escort-contracts", title: "ITHA Escort Contract Success", messageKey: "contract_success_nokill", background :"escort_contracts_bkg2.png"});
		}
		else if (missionVariables.ec_missionkills === 1)
		{
			mission.runScreen ({screenID:"escort-contracts", title: "ITHA Escort Contract Success", messageKey: "contract_success_onekill", background :"escort_contracts_bkg2b.png"});
		}
		else if (missionVariables.ec_missionkills > 1)
		{
			mission.runScreen ({screenID:"escort-contracts", title: "ITHA Escort Contract Success", messageKey: "contract_success", background :"escort_contracts_bkg2c.png"});
		}
		delete missionVariables.ec_mothername;
		delete missionVariables.ec_targetsystem;
		delete missionVariables.ec_payment;
		delete missionVariables.ec_missionkills;
		delete missionVariables.ec_targetsystemname;
	}
	else if (this.ec_runcontractsetup)
		{delete this.ec_runcontractsetup;}
};

//  the following functions control the offering and display of the available new contracts on mission screens.		
//  sets up arrays containing the info needed for potential contracts

this.$EscortF4Interface = function(w) 
{

	// Various validity checks including random factor modified by escortrep to see if contracts will be offered.
	if (player.ship.docked && player.ship.dockedStation.isMainStation && player.ship.equipmentStatus("EQ_ESCORTCONTRACTS") === "EQUIPMENT_OK"
		&& this.ec_runcontractsetup && player.bounty === 0 && (this.$ccChooseAll || Math.random() < (this.$ccFactor*(1+(this.ec_escortrep / 20)))) )  
	{ 
    	if (this.$debugMode) log("Escort Contracts: contracts available"); 	

		// get IDs of all systems in jump range
		var systemsInRange = SystemInfo.systemsInRange(7); 	// array of systemInfos within 7 light years
		var systemsInRangeID = new Array(); 				// array to hold ID's only of systems in range
		var counter = 0;
		for (counter = 0; counter < systemsInRange.length; counter++) 
			{systemsInRangeID[counter] = systemsInRange[counter].systemID;}

		// select only some of the systems in range 
		this.ec_targetsystems = new Array(); 				// array to hold IDs of possible contract destinations.
		var thisGalaxy 	= system.info.galaxyID;              
		var tempcounter = 0;
		var targetGovernment = 0;
		var maxGovernment = 0;
		var systemProbability = 0;
		for (counter = 0; counter < systemsInRangeID.length; counter++) 
		{
			targetGovernment = System.infoForSystem(thisGalaxy,systemsInRangeID[counter]).government; 
			if (this.$ccOldStyle) 
			{
				// Select only systems with lower government types than the current system. 
				systemProbability = this.$ccSystemFactor;  // standard value is 0.5
				maxGovernment = system.government - 1;
			}
			else
			{
				// Select systems with government type $ccMaxGovType or lower. Safer systems have a lower probability of being chosen. 
				systemProbability = this.$ccSystemFactor * (1 - targetGovernment/10);
				maxGovernment = this.$ccMaxGovType;
			}		
			if (targetGovernment <= maxGovernment && (this.$ccChooseAll || Math.random() < systemProbability) && !System.infoForSystem(thisGalaxy,systemsInRangeID[counter]).sun_gone_nova)
			{
				// add system to the contract offers 
				this.ec_targetsystems[tempcounter] = systemsInRangeID[counter];
				tempcounter++;
				if (this.$debugMode) log("Escort Contracts: target system " + System.infoForSystem(thisGalaxy, systemsInRangeID[counter]).name + ", government: " + targetGovernment + ", probability: " + systemProbability + ", chosen"); 	
			}
			else if (this.$debugMode) 
			{
				log("Escort Contracts: target system " + System.infoForSystem(thisGalaxy, systemsInRangeID[counter]).name + ", government: " + targetGovernment + ", probability: " + systemProbability + ", not chosen"); 	
			}
		}

		// Because of the random factors, the target list can be empty. 
		// In this case no contract interface is visible at all (fix 1.7.1).
		if (tempcounter === 0)
		{	
			player.ship.dockedStation.setInterface("escort_f4contracts",null);
			if (this.$debugMode) log("Escort Contracts: target list empty"); 	
		}
		else
		{	
			// create and populate other arrays to hold contract info for each potential target system
			missionVariables.ec_numbertargetsystems = this.ec_targetsystems.length;
			this.ec_targetsystemsgovernment = new Array(this.ec_targetsystems.length);
			this.ec_targetsystemseco = new Array(this.ec_targetsystems.length);
			this.ec_targetsystemstech = new Array(this.ec_targetsystems.length);
			this.ec_targetsystemsnames = new Array(this.ec_targetsystems.length);
			this.ec_mothernames = new Array(this.ec_targetsystems.length);
			this.ec_contractbaseprices = new Array(this.ec_targetsystems.length);
			this.ec_contractactualprices = new Array(this.ec_targetsystems.length);
			this.ec_killsbonuses = new Array(this.ec_targetsystems.length);
			counter = 0;
			for (counter =0;counter < this.ec_targetsystems.length;counter++)
			{
				this.ec_targetsystemsgovernment[counter] = System.infoForSystem(thisGalaxy, this.ec_targetsystems[counter]).government;
				this.ec_targetsystemseco[counter] = System.infoForSystem(thisGalaxy, this.ec_targetsystems[counter]).economy;
				this.ec_targetsystemstech[counter] = System.infoForSystem(thisGalaxy, this.ec_targetsystems[counter]).techlevel;
				this.ec_targetsystemsnames[counter] = System.infoForSystem(thisGalaxy, this.ec_targetsystems[counter]).name;
				this.ec_mothernames[counter] = randomName();
				this.ec_contractbaseprices[counter] = (75 + (75 * (7 - this.ec_targetsystemsgovernment[counter])));  
				this.ec_contractactualprices[counter] = Math.round(this.ec_contractbaseprices[counter] * (0.51 + Math.random() + (this.ec_escortrep / 20)));
				this.ec_killsbonuses[counter] = Math.round(40 * (0.51 + Math.random() + (this.ec_escortrep / 20)));
			}
			player.ship.dockedStation.setInterface("escort_f4contracts",
			{
				title: "ITHA Escort Contracts (" +missionVariables.ec_numbertargetsystems+" available)",
				category: "Escort Jobs",
				summary: "Independent Traders & Hauliers Association pilots will pay you to escort them through a nearby system. You must launch within 3 hours of accepting a contract.",
				callback: this.ec_displaymissionScreens.bind(w)
			});
		}	
	}   
	else 
	{
		//remove interface if contract accepted or no offers
		player.ship.dockedStation.setInterface("escort_f4contracts",null);
    	if (this.$debugMode) log("Escort Contracts: no contracts available"); 	
	}
};

// shows mission screen for selecting between available contracts
this.ec_displaymissionScreens = function()
{
	if (this.$isBigGuiActive() == false && player.ship.hudHidden == false) 
	{
		player.ship.hudHidden = true; // added with version 1.6.3 to make room on the map screen
	}
	
	if(missionVariables.ec_numbertargetsystems === 1)
	{	
        this.$resetTargetSystem = player.ship.targetSystem;   // save it because it will be changed when using the map
		this.ec_contractno = 0;
		this.ec_updatemissionvariables();
		mission.runScreen({title: "Escort Contract: Trader " +(this.ec_mothernames[this.ec_contractno]), messageKey: "contract_info", choicesKey: "agree_exit", initialChoicesKey: "2_VIEW_CHART", model: "ITHAcoin", background: "escort_contracts_bkg4.png", overlay : "welcomeCom.png"}, this.ec_contractchoice2.bind(this)); 
	}
	else if (missionVariables.ec_numbertargetsystems > 1)
	{
        this.$resetTargetSystem = player.ship.targetSystem;   // save it because it will be changed when using the map
		this.ec_contractno = 0;
		this.ec_updatemissionvariables();
		mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_info", choicesKey: "agree_exit_next_previous", initialChoicesKey: "2_NEXT", model: "ITHAcoin", background: "escort_contracts_bkg4.png", overlay : "welcomeCom.png"}, this.ec_contractchoice4.bind(this));
	}
	else if (this.ec_leftScreen)
	{
		player.consoleMessage("Escort Contracts - All Independent Traders have now secured an escort. No contracts currently available.",10);
		delete this.ec_leftScreen;
		player.ship.dockedStation.setInterface("escort_f4contracts",null); //remove interface if contract accepted or no offers
	}
	else 
	{
		player.ship.dockedStation.setInterface("escort_f4contracts",null); //remove interface if contract accepted or no offers
	}
};

// function used if player leaves mission screens without accepting a contract ("decline all")
this.ec_leaveScreen = function()
{
	player.ship.hudHidden = false;                       // show HUD again (added 1.6.3) 
	player.ship.targetSystem = this.$resetTargetSystem;  // reset previously selected target system (added 1.7.1)                 

	this.ec_updatecontractsTime = clock.minutes + (60 + Math.round(Math.random()*660));
	if (!this.ec_pendingScreenTimer)
	{
		this.ec_pendingScreenTimer = new Timer(this, this.ec_screenCheck, 1, 3);
	}
	else 
	{
		this.ec_pendingScreenTimer.start();
	}
};

// mission screen choices for 1 contract available (without next and previous options).
this.ec_contractchoice2 = function(choice)
{
	if (this.$isBigGuiActive() == false && player.ship.hudHidden == false) 
	{
		player.ship.hudHidden = true;  // added 1.6.3
	}

	switch (choice)
	{
		case "1_AGREE":
			this.ec_currentcontract = true;
			this.ec_contractexpiretime = clock.minutes + 180;
			this.ec_missionkills = 0;
			this.ec_payment = 0;
			this.ec_mothername = missionVariables.ec_mothername;
			this.ec_targetsystem = missionVariables.ec_targetsystem;
			this.ec_contractactualprice = missionVariables.ec_dbcontractactualprice;
			this.ec_killsbonus = missionVariables.ec_dbkillsbonus;
			player.ship.hudHidden = false;                                      // show HUD again (fix 1.7.1) 	
			mission.runScreen({screenID:"escort-contracts", title: "ITHA Escort Contract Agreed", messageKey: "contract_agreed", background :"escort_contracts_bkg3.png"});
			mission.setInstructionsKey ("Contract_Details", "Escort_Contracts");
			this.ec_cleanupmissionVariables();
			delete this.ec_pendingScreenTimer;
			player.ship.dockedStation.setInterface("escort_f4contracts",null);  //remove interfaces option after acceptance
			player.ship.targetSystem = this.$resetTargetSystem;                 
			break;
		case "2_VIEW_CHART":
			player.ship.targetSystem = missionVariables.ec_targetsystem;
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract: Trader " +(this.ec_mothernames[this.ec_contractno]), messageKey: "contract_chart", choicesKey: "chart_agree_exit", initialChoicesKey: "2_RETURN", background: "contracts_chart.png", backgroundSpecial: "SHORT_RANGE_CHART"}, this.ec_contractchoice2.bind(this));
			break;
		case "2_RETURN":
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract: Trader " +(this.ec_mothernames[this.ec_contractno]), messageKey: "contract_info", choicesKey: "agree_exit", initialChoicesKey: "2_VIEW_CHART", background :"escort_contracts_bkg.png"}, this.ec_contractchoice2.bind(this));
			break;
		case "3_EXIT_RETURN":
			this.ec_leaveScreen();
			break;
		default: 
			this.ec_cleanupmissionVariables();
			player.ship.dockedStation.setInterface("escort_f4contracts",null); //remove interfaces option after rejection, though these lines never should happen
	}		
};

// mission screen choices for 2 or more contracts available (including next and previous options).
this.ec_contractchoice4 = function(choice)
{
	if (this.$isBigGuiActive() == false && player.ship.hudHidden == false)
	{
		player.ship.hudHidden = true;  // added 1.6.3
	}

	switch (choice)
	{
		case "1_AGREE":
			this.ec_currentcontract = true;
			this.ec_contractexpiretime = clock.minutes + 180;
			this.ec_missionkills = 0;
			this.ec_payment = 0;
			this.ec_mothername = missionVariables.ec_mothername;
			this.ec_targetsystem = missionVariables.ec_targetsystem;
			this.ec_contractactualprice = missionVariables.ec_dbcontractactualprice;
			this.ec_killsbonus = missionVariables.ec_dbkillsbonus;
			player.ship.hudHidden = false;                                      // show HUD again (fix 1.7.1)	
			mission.runScreen({screenID:"escort-contracts", title: "ITHA Escort Contract Agreed", messageKey: "contract_agreed", background :"escort_contracts_bkg3.png"});
			mission.setInstructionsKey ("Contract_Details", "Escort_Contracts");
			this.ec_cleanupmissionVariables();
			delete this.ec_pendingScreenTimer;
			player.ship.dockedStation.setInterface("escort_f4contracts",null);  //remove interfaces option after acceptance
			player.ship.targetSystem = this.$resetTargetSystem;                 
			break;
		case "2_NEXT":
			this.ec_contractno++;
			if (this.ec_contractno === missionVariables.ec_numbertargetsystems)
			{this.ec_contractno = 0;}
			this.ec_updatemissionvariables();
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_info", choicesKey: "agree_exit_next_previous", initialChoicesKey: "2_NEXT", background: "escort_contracts_bkg.png"}, this.ec_contractchoice4.bind(this));
			break;
		case "3_PREVIOUS":
			this.ec_contractno--;
			if (this.ec_contractno < 0 )
			{this.ec_contractno = (missionVariables.ec_numbertargetsystems - 1);}
			this.ec_updatemissionvariables();
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_info", choicesKey: "agree_exit_next_previous", initialChoicesKey: "3_PREVIOUS", background: "escort_contracts_bkg.png"}, this.ec_contractchoice4.bind(this));
			break;
		case "2_NEXTCHART":
			this.ec_contractno++;
			if (this.ec_contractno === missionVariables.ec_numbertargetsystems)
			{this.ec_contractno = 0;}
			this.ec_updatemissionvariables();
			player.ship.targetSystem = missionVariables.ec_targetsystem;
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_chart", choicesKey: "chart_agree_exit_next_previous", initialChoicesKey: "2_NEXTCHART", background: "contracts_chart.png", backgroundSpecial: "SHORT_RANGE_CHART"}, this.ec_contractchoice4.bind(this));
			break;
		case "3_PREVIOUSCHART":
			this.ec_contractno--;
			if (this.ec_contractno < 0 )
			{this.ec_contractno = (missionVariables.ec_numbertargetsystems - 1);}
			this.ec_updatemissionvariables();
			player.ship.targetSystem = missionVariables.ec_targetsystem;
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_chart", choicesKey: "chart_agree_exit_next_previous", initialChoicesKey: "3_PREVIOUSCHART", background: "contracts_chart.png", backgroundSpecial: "SHORT_RANGE_CHART"}, this.ec_contractchoice4.bind(this));
			break;
	    case "4_VIEW_CHART":
			player.ship.targetSystem = missionVariables.ec_targetsystem;
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_chart", choicesKey: "chart_agree_exit_next_previous", initialChoicesKey: "4_RETURN", background: "contracts_chart.png", backgroundSpecial: "SHORT_RANGE_CHART"}, this.ec_contractchoice4.bind(this));
			break;
	    case "4_RETURN":
			mission.runScreen({screenID:"escort-contracts", title: "Escort Contract " +(this.ec_contractno+1)+": Trader " +this.ec_mothernames[this.ec_contractno], messageKey: "contract_info", choicesKey: "agree_exit_next_previous", initialChoicesKey: "4_VIEW_CHART", background :"escort_contracts_bkg.png"}, this.ec_contractchoice4.bind(this));
			break;
	    case "5_EXIT_RETURN":		
			this.ec_leaveScreen();	
			break;
		default:
			this.ec_cleanupmissionVariables();
			player.ship.dockedStation.setInterface("escort_f4contracts",null); //remove interfaces option after rejection, though this line never should happen		
	}
};

// used to display prompt messages if player has left mission screens.
this.ec_screenCheck = function()
{
	if (!player.ship.docked)
	{
		this.ec_pendingScreenTimer.stop();
		return;
	}
};

// returns player to missionscreens having looked at charts.
this.guiScreenChanged = function(to,from)
{
	if (this.ec_pendingScreenTimer)
	{
		this.ec_pendingScreenTimer.stop();
		if (clock.minutes > this.ec_updatecontractsTime) // if player has been away from mission screens for some time re-evaluate contract offers.
		{
			this.ec_leftScreen = true;
			delete this.ec_pendingScreenTimer;
			this.ec_cleanupmissionVariables();
			player.ship.dockedStation.setInterface("escort_f4contracts",null); //remove interfaces option after timeout
		}
		return;
	}
};

// called to create missionVariables for display on the mission screens.
this.ec_updatemissionvariables = function ()
{
	missionVariables.ec_mothername = 			this.ec_mothernames[this.ec_contractno];
	missionVariables.ec_targetsystem = 			this.ec_targetsystems[this.ec_contractno];
	missionVariables.ec_dbcontractactualprice = this.ec_contractactualprices[this.ec_contractno];
	missionVariables.ec_targetsystemname = 		this.ec_targetsystemsnames[this.ec_contractno];
	missionVariables.ec_dbkillsbonus = 			this.ec_killsbonuses[this.ec_contractno];
	missionVariables.ec_targetgovsymbol = 		String.fromCharCode(this.ec_targetsystemsgovernment[this.ec_contractno]);
	missionVariables.ec_targetecosymbol = 		String.fromCharCode(23-this.ec_targetsystemseco[this.ec_contractno]);
	missionVariables.ec_targettech = 			this.ec_targetsystemstech[this.ec_contractno]+1;
	missionVariables.ec_currentsystemname = 	system.name;
	missionVariables.ec_currentsystemgov = 		String.fromCharCode(system.government);
	missionVariables.ec_currentsystemtech = 	system.techLevel+1;
	missionVariables.ec_currentsystemeco = 		String.fromCharCode(23-system.economy);
		
	switch (this.ec_targetsystemsgovernment[this.ec_contractno])
	{
		case 0:
			missionVariables.ec_contractdifficultydesc = "very high";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,8,8,8,8);
			missionVariables.ec_targetsystemgovernmentname = "an anarchy";
			break;
		case 1:
			missionVariables.ec_contractdifficultydesc = "high";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,8,8,8,24);
			missionVariables.ec_targetsystemgovernmentname = "a feudal state";
			break;
		case 2:
			missionVariables.ec_contractdifficultydesc = "moderate";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,8,8,24,24);
			missionVariables.ec_targetsystemgovernmentname = "a multi-government system";
			break;
		case 3:
			missionVariables.ec_contractdifficultydesc = "moderate";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,8,8,24,24);
			missionVariables.ec_targetsystemgovernmentname = "a dictatorship";
			break;
		case 4:
			missionVariables.ec_contractdifficultydesc = "low";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,8,24,24,24);
			missionVariables.ec_targetsystemgovernmentname = "a communist system";
			break;
		case 5:
			missionVariables.ec_contractdifficultydesc = "low";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,8,24,24,24);
			missionVariables.ec_targetsystemgovernmentname = "a confederacy";
			break;
		case 6:
			missionVariables.ec_contractdifficultydesc = "very low";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,24,24,24,24);
			missionVariables.ec_targetsystemgovernmentname = "a democracy";
			break;
		case 7:
			missionVariables.ec_contractdifficultydesc = "very low";
			missionVariables.ec_contractdifficultystars = String.fromCharCode(8,24,24,24,24);
			missionVariables.ec_targetsystemgovernmentname = "a corporate state";
			break;
		// case 7 couldn't happen in versions before 1.7.1 because the target system goverment was 
		// always at least 1 lower than the current government. With 1.7.1 this was changed, 
		// but now government 6 and 7 systems are excluded completely. But since this is user-configurable, 
		// somebody could chose to include type 7 systems, so better be safe. 	
	}		
};

// clean up mission variables after contract mission screens.
this.ec_cleanupmissionVariables = function()
{
	delete missionVariables.ec_contractdifficultydesc;
	delete missionVariables.ec_contractdifficultystars;
	delete missionVariables.ec_numbertargetsystems;
	delete missionVariables.ec_targetsystem;
	delete missionVariables.ec_targetsystemgovernmentname;
	delete missionVariables.ec_targetsystemname;
	delete missionVariables.ec_dbcontractactualprice;
	delete missionVariables.ec_dbkillsbonus;
	delete missionVariables.ec_mothername;
	delete missionVariables.ec_targetgovsymbol;
	delete missionVariables.ec_currentsystemeco;
	delete missionVariables.ec_targetecosymbol;
	delete missionVariables.ec_currentsystemname;
	delete missionVariables.ec_currentsystemgov;
	delete missionVariables.ec_currentsystemtech;
	delete missionVariables.ec_currentsystemeco;
	delete missionVariables.ec_targettech;
};

// event handler function to spawn the mother on player launch if she does not
// exist but there is a current contract.
this.shipWillLaunchFromStation = function() 
{
    if (this.ec_currentcontract && this.ec_currentcontract !== "success") 
	{
        if (system.countShipsWithRole("ec_mother") === 0 && clock.minutes < this.ec_contractexpiretime) 
		{
            this.mother = system.addShips("ec_mother", 1, system.mainStation.position.subtract(system.mainStation.vectorForward.multiply(14000)),4000)[0];
		} 
		else 
		{
            // if more than 3 game hours passed since agreeing contract - fail contract.
            player.consoleMessage("Escort Contract - You failed to launch within the allocated time window. Your mother has already departed. Contract terminated.",10);
            delete this.ec_currentcontract;
            mission.setInstructionsKey (null, "Escort_Contracts");
            this.ec_escortrep--;
            if (this.ec_escortrep < -10) 
			{
                this.ec_escortrep = -10;
            }
        }
    }
};

// event handler driven function to fail the contract if the player exits
// witchspace into a system where the mother is not present. Also controls
// gradual reduction of positive or negative escort reputation in between
// contracts.
this.shipExitedWitchspace = function()
{	
	if (system.countShipsWithRole("ec_mother") === 0 && this.ec_currentcontract) 
	{
		// wrong system? 
		if (this.ec_currentcontract === "success") 
		{
			// case where mother safely docked, but player saved at OXP station and never collected reward.
			delete this.ec_currentcontract;   
		} 		
		else 
		{
			// It is possible that the ship following though a wormhole arrives some seconds 
			// earlier than the ship that had opened the wormhole. If this happens during
			// escorting, it will break the mission, so we have to accept a missing mother 
			// at this point, if the system is the right one. (fix 1.7.1)
			if (system.info.systemID !== this.ec_targetsystem)
			{
				// Player jumped definitely to the wrong system! Contract terminated immediately. 
				this.ec_failedtofollowmother(); 
				if (this.$debugMode) log("Escort Contracts: player jumped to wrong system"); 	
			}
			else
			{
				// Correct system but no mother present. Either the wormhole arrival order was wrong
				// or the player has jumped on his own _before_ mother. In the latter case, mother 
				// will never show up, and the contract will be terminated at docking. 
				if (this.$debugMode) log("Escort Contracts: player early or has jumped on his own"); 	
			}
		}
	}

	if (!this.ec_currentcontract && Math.random() < 0.1) // no contract - random (10%) chance of reputation erosion.
		{this.ec_escortrep = Math.round(this.ec_escortrep * 0.8);}
    
	if (!this.ec_currentcontract && system.countShipsWithRole("ec_mother") !== 0) // if contract has failed, but there is a mother in the system get rid of her.
		{this.mother.remove(true);}	
};

// called from various points within the worldscript if the player has not managed to follow the mother to the target system.
this.ec_failedtofollowmother = function()
{
	delete this.ec_currentcontract;
	mission.setInstructionsKey (null, "Escort_Contracts");
	this.ec_escortrep--;
	if (this.ec_escortrep < -10)
		{this.ec_escortrep = -10;}
	player.consoleMessage("Escort Contract - You have failed to follow your mother-ship to the target system. Contract terminated.",10);
};

// keeps a record of hostile pirate kills during the course of the contract. Only kills that occur within 2 scanner ranges of the mother count.
this.shipTargetDestroyed = function(target)
{
	if(this.mother && this.mother.isValid && this.ec_currentcontract && player.ship.position.distanceTo(this.mother.position) < (this.mother.scannerRange * 3))
	{
		if(target.bounty > 0 && !target.isRock && !target.isCargo) // only kills that have a bounty and ain't rocks or cargo count!
		{
			this.ec_missionkills++;
			// message only if the killed pirate was the only/last one in scanner range 
			// note: The player will only see/hear the message when he is in scanner range. 
			if (!player.alertHostiles)
				{this.mother.commsMessage("[ec_comms_pirate_killed]");}
		}
	}
};

// timer called from mother shipscript on the mother entering witchspace to the target system. Fails the contract if the player fails to follow before the wormhole expires. 
this.ec_motherexitedsystem = function(mothermass)
{
	var expirytime = 50 + (mothermass/4000); // same formula as used by core game code to calculate wormhole expiry.
	this.wormholetimer = new Timer (this, this.ec_checkforfollowedmother, expirytime);
};

// called by timer at wormhole expiry. If player is still in original system contract has failed.
this.ec_checkforfollowedmother = function()
{
	if(this.ec_currentcontract)
	{
		if (system.countShipsWithRole("ec_mother") === 0) 
			{this.ec_failedtofollowmother();}
	}
};

// returns true if a HUD with allowBigGUI is enabled, otherwise false (added in version 1.6.3)
this.$isBigGuiActive = function() 
{
	if (oolite.compareVersion("1.83") < 0) 
	{
		return player.ship.hudAllowsBigGui;
	}
	else 
	{
		var bigGuiHUD = ["XenonHUD.plist"]; 	// until there is a property we can check, I'll be listing HUD's that have the allow_big_gui property set here
		if (bigGuiHUD.indexOf(player.ship.hud) >= 0) 
		{
			return true;
		} 
		else 
		{
			return false;
		}
	}
}

// replaced in version 1.7.1  by damage_probability = 0 in equipment.plist
// function to stop the Escort License being damaged in combat.
//this.equipmentDamaged = function(equipment)
//{
//	if(equipment === "EQ_ESCORTCONTRACTS")
//	{
//		player.ship.setEquipmentStatus(equipment,"EQUIPMENT_OK");
//	}
//};

Scripts/Escort_Contracts_Rep.js
"use strict";
this.name        = "Escort_Contracts_Rep"; 
this.version     = "1.7.1"; 
this.author      = "capt murphy"; 
this.copyright   = "2011 capt murphy";
this.licence     = "CC BY-NC-SA 3.0"; // see http://creativecommons.org/licenses/by-nc-sa/3.0/ for more info.
this.description = "Escort Contracts - worldscript to display reputation."; 

// This is an event handler driven function that updates the manifest escort reputation text.
this.guiScreenWillChange = function(to, from)
{
	if (to !=="GUI_SCREEN_MANIFEST" || player.ship.equipmentStatus("EQ_ESCORTCONTRACTS") !== "EQUIPMENT_OK")
		{return;}

	var escortrepstring = "Your I.T.H.A. escort reputation is ";  
	if (worldScripts["Escort_Contracts"].ec_escortrep < -5)
		{escortrepstring += "very poor.";}
	else if (worldScripts["Escort_Contracts"].ec_escortrep < 0)
		{ escortrepstring += "poor.";}
	else if (worldScripts["Escort_Contracts"].ec_escortrep > 10)
		{escortrepstring += "excellent.";}
	else if (worldScripts["Escort_Contracts"].ec_escortrep > 5)
		{ escortrepstring += "very good.";}
	else if (worldScripts["Escort_Contracts"].ec_escortrep > 0)
		{ escortrepstring += "good.";}
	else
		{escortrepstring += "neutral.";}

	mission.setInstructions(escortrepstring, "Escort_Contracts_Rep");
}
Scripts/ec_mother.js
"use strict";
this.name        = "Escort_Contracts_Mother"; 
this.version     = "1.7.1";      // 2015-12-04
this.author      = "capt murphy"; 
this.copyright   = "2011 capt murphy";
this.licence     = "CC BY-NC-SA 3.0"; // see http://creativecommons.org/licenses/by-nc-sa/3.0/ for more info.
this.description = "Escort Contracts mother shipscript."; 

this.$debugMode = 0;  // write debug information to logfile?

// this script contains several event handler driven functions and script defined functions used to control the mother's AI. Many of the script defined functions are called from the various ec_mother*AI.plists and send messages back to these AI.plists - the code should be considered alongside these AI.plists.

// functions used in the starting system. ec_mother1AI.plist is the main AI used in the starting system.
this.shipSpawned = function()
{
	
	// Don't change a name already assigned by Randomshipnames or any other naming OXP.
	// Checking conditions taken from Randomshipnames
	// Usually this is called before the world script "shipSpawned" functions, but better be safe...
    if(ship.displayName.substr(0, 4) !== "RRS " && ship.displayName.indexOf(":") === -1 && ship.displayName.indexOf("~") === -1 && ship.displayName.indexOf(" - ") === -1) 
	{	
		var oldDisplayName = ship.displayName;
		this.ship.displayName = worldScripts["Escort_Contracts"].ec_mothername +"'s " + ship.name;  // "Trader" removed, this made the "ship name" longer than necessary (1.7.1)
		if(this.$debugMode) log ("Escort contracts: name assigned: " + this.ship.displayName + ", oldDisplayName: " + oldDisplayName);
	}	
	else
	{	
		if(this.$debugMode) log ("Escort contracts: name unchanged: " + this.ship.displayName);
	}
	this.ship.setAI ("ec_mother1AI.plist");
	this.ec_playerwarnings = 0;
	this.ec_playerinflicteddamage = 0;
	this.ec_suppresslowenergyalert = 0;
	this.ec_suppressoutofrange = 0;
	this.ec_suppressapproach = 0;
	this.ec_suppressunderattack = 0;    // added with version 1.7.1
	this.ec_suppresstorustooclose = 0;  // added with version 1.7.1
	this.ec_suppresstorusmismatch = 0;  // added with version 1.7.1
	
}

// functions called by ec_mother1AI.plist.

// sets the mothers destination in starting system.
this.ec_getCoordinates = function()
{
	this.ship.savedCoordinates = system.mainStation.position.subtract(system.mainStation.vectorForward.multiply(36000));
	this.ship.reactToAIMessage("GOT_COORD");
}

// checks to see how close player is prior to initialising witchspace jump to target system.
this.ec_monitorplayerlocation = function()
{
	if(!player.ship || player.ship.docked === true)
	{
		this.ship.reactToAIMessage("TARGET_LOST");
		return;
	}
	
	var playerDistance = this.ship.position.distanceTo(player.ship.position);
	if(playerDistance > this.ship.scannerRange * 4) // if player is ignoring comms to approach for witch jump leave anyway
	{
		player.commsMessage(expandDescription("[ec_comms_leaving_without]"));
        this.ship.reactToAIMessage("PLAYER_CLOSE");
	}
	else if (playerDistance > 9000)		
	{
		this.ship.reactToAIMessage("PLAYER_FAR");   // will result in repeated messages to approach
	} 
	else
	{
		this.ship.reactToAIMessage("PLAYER_CLOSE"); // will result in mother attempting to jump to the target system.
	}	
}		

// initiates the mother witchspace jump to the target system and calls a timer in the OXP's worldscript to check if the player has followed.
this.ec_motherjumptotarget = function()
{
	this.ship.fuel = 7;
	var jumpsuccess = this.ship.exitSystem(worldScripts["Escort_Contracts"].ec_targetsystem);
	if (!jumpsuccess)
		{this.ship.reactToAIMessage("JUMP_FAILED");}
	else
		{worldScripts["Escort_Contracts"].ec_motherexitedsystem(this.ship.mass);}
}

// player comms functions used in starting system.
this.ec_prepareforjump = function()
{this.ship.commsMessage("[ec_comms_please_approach]");}

this.ec_abouttojump = function()
{this.ship.commsMessage("[ec_comms_init_witchspace]");}

this.ec_motherjumpfailed = function()
{this.ship.commsMessage("[ec_comms_witchspace_failed]");}	

// called post witchspace jump to check if mother is in target system - if so the AI switches to ec_mother2AI.plist which is the main AI used in the target system.
this.ec_checksystem = function()
{
	this.ship.fuel = 7;
	this.ec_motherfrustration = 0;
	this.ec_playerwarnings = 0;
	this.ec_playerinflicteddamage = 0;
	this.ec_suppresslowenergyalert = 0;
	if (system.info.systemID === worldScripts["Escort_Contracts"].ec_targetsystem)
		{this.ship.reactToAIMessage("TARGET_SYSTEM_TRUE");}
	else
		{this.ship.reactToAIMessage("TARGET_SYSTEM_FALSE");}
}

// functions to ensure the mother matches the player's max speed when the player.ship is slower than the mother. Called from the AI.
this.ec_setDesiredSpeed = function()
{if (this.ship.maxSpeed >= player.ship.maxSpeed*0.95){this.ship.desiredSpeed = (player.ship.maxSpeed*0.95);} else {this.ship.desiredSpeed = this.ship.maxSpeed;}}

this.ec_setDesiredSpeedInject = function()
{if (this.ship.maxSpeed >= player.ship.maxSpeed*0.95){this.ship.desiredSpeed = (player.ship.maxSpeed*6.65);} else {this.ship.desiredSpeed = (this.ship.maxSpeed*7);}}

// the following functions are used once the mother is in the target system and also in combat situations.

// event handler driven function - dual role - defines communication and mother actions if repeatedly 
// attacked by player and also communication when mother is taking damage from other hostiles.
this.shipTakingDamage = function(amount, whom, type)
{  
	if (whom === player.ship)
	{
		if (this.ec_playerinflicteddamage === 0)
			{this.ec_playerwarnings = 0;}
		this.ec_playerinflicteddamage += amount;
		if (this.ec_playerwarnings === 1 && this.ec_playerinflicteddamage > (this.ship.maxEnergy * 0.50))
			{this.ec_playerwarnings = 2;}
		if (this.ec_playerwarnings === 3 && this.ec_playerinflicteddamage > (this.ship.maxEnergy * 0.75))
			{this.ec_playerwarnings = 4;}
	
		switch (this.ec_playerwarnings)
		{
			case 0:
			    // message depending on damage type (fg, 2015-11-05)
				if (type === "scrape damage")    
					this.ship.commsMessage("[ec_comms_scrape_damage_1]");      
				else 
					this.ship.commsMessage("[ec_comms_friendly_fire_1]");
					
				this.ec_playerwarnings = 1;
				break;
			case 1:
				break;
			case 2:
			    // message depending on damage type (fg, 2015-11-05)
				if (type === "scrape damage")    
					this.ship.commsMessage("[ec_comms_scrape_damage_2]");      
				else 
					this.ship.commsMessage("[ec_comms_friendly_fire_2]");

				this.ec_playerwarnings = 3;
				break;
			case 3:
				break;
			case 4:
				this.ship.commsMessage("[ec_comms_enough]");
				this.ec_playerwarnings = 5;
				this.ship.target = player.ship;
				this.ship.setAI("ec_killplayerAI.plist"); 							// very simple AI - mother will attempt to kill player - no fleeing.
				delete worldScripts["Escort_Contracts"].ec_currentcontract;
				worldScripts["Escort_Contracts"].ec_escortrep -= 2;
				if (worldScripts["Escort_Contracts"].ec_escortrep < -10)
					{worldScripts["Escort_Contracts"].ec_escortrep = -10;}
				mission.setInstructionsKey (null, "Escort_Contracts");
			case 5:
				break;
		}
	}
	else if (this.ship.energy < (this.ship.maxEnergy * 0.67) && this.ec_suppresslowenergyalert === 0)
	{
		this.ship.commsMessage("[ec_comms_taking_damage_1]");
		this.ec_suppresslowenergyalert = 1;
	}
	else if (this.ship.energy < (this.ship.maxEnergy * 0.33) && this.ec_suppresslowenergyalert === 1)
	{
		this.ship.commsMessage("[ec_comms_taking_damage_2]");
		this.ec_suppresslowenergyalert = 2;
	}
}

// event handler driven function - turns back on communication alerts if previously suppressed by above function.
this.shipEnergyBecameFull = function()
{this.ec_suppresslowenergyalert = 0;}

// called from intercept AI to check for friendly fire from player or an player's escort from hired guns OXP.
this.ec_friendlyfirecheck = function()
{
	if(this.ship.target && ((this.ship.target.isPlayer && this.ec_playerwarnings !== 5) || (this.ship.target.hasRole("hiredGuns_escort") && this.ec_playerwarnings !== 5)))
	{
		this.ship.target = null;
		this.ship.reactToAIMessage("FRIENDLY_FIRE");
	}
	else
	{
		this.ship.reactToAIMessage("ENEMY_FIRE");
	}
}

// called from ec_mothergroupjump.plist  - controls synchronised jump drive behaviour.
this.ec_checkplayermasslock = function()
{

	if (this.ship.position.distanceTo(player.ship.position) > this.ship.scannerRange)
	{
		if (player.alertCondition !=3)
		{
			if (this.ec_suppressoutofrange === 0) // do not issue comms every iteration. Frequency depends on government type.
				{player.commsMessage(expandDescription("[ec_comms_outside_scanner_range]"));}
			this.ec_suppressoutofrange++;
			if (this.ec_suppressoutofrange >= (8+system.government)*2)    // *2 added 1.7.1, frequency was still too high for eSpeak
				{this.ec_suppressoutofrange = 0;}
		}
		this.ship.reactToAIMessage ("NO_JUMP");
		// if player is out of range there is a chance that the mother will be ambushed by pirates. Higher in more dangerous government types.
		if ((Math.random()*200)<(14-(system.government*2)) && system.countShipsWithPrimaryRole("pirate",this.ship,this.ship.scannerRange) === 0)
			{system.addGroup("pirate",1+Math.round(Math.random()*2),this.ship.position, 1000);}
		return;
	}
	if (player.alertMassLocked || this.ship.speed > this.ship.maxSpeed)
    {
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;} // this variable suppresses the Jump Drive Synchronised message - reset on a masslock event
		this.ec_suppressoutofrange = 0;
		return;
	}
	var targets = system.filteredEntities(this, ec_checkformasslock, this.ship, this.ship.scannerRange);
	if (targets.length > 0)
	{
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) {delete this.ec_suppresssynchedjumpdrive;}
			this.ec_suppressoutofrange = 0;
		return;
	}
	if(this.ship.position.distanceTo(player.ship.position) > 2500)
	{
		if (this.ec_suppressapproach === 0)
			{this.ship.commsMessage("[ec_comms_torus_synchronise]");}
		this.ec_suppressapproach++
		if (this.ec_suppressapproach >= 10)
			{this.ec_suppressapproach = 0;}
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;}
		this.ec_suppressoutofrange = 0;
		this.ec_suppresstorustooclose = 0;
		return;
	}
	if(this.ship.position.distanceTo(player.ship.position) > 500 && this.ship.heading.dot(player.ship.heading) > 0.98)
	{
		if (this.ec_motherfrustration > 0)
			{this.ec_motherfrustration--;}
		if (!this.ec_suppresssynchedjumpdrive)
		{
			this.ship.commsMessage("[ec_comms_torus_start]"); 
			this.ec_suppresssynchedjumpdrive = true; // only give message when drive is first synchronised
		}	
		this.ec_jumpCallback = addFrameCallback(this.ec_matchVelocity.bind(this));
		this.ship.velocity = this.ship.heading.multiply(this.ship.maxSpeed * 32); // velocity boost to mother to simulate torus speeds.
		// reset message repeat counters - if they are torus driving, everything must be ok.
		this.ec_suppresstorustooclose = 0;
		this.ec_suppresstorusmismatch = 0;
		this.ec_suppressunderattack = 0;
		return;
	}
	if (this.ship.position.distanceTo(player.ship.position) < 500)
	{
		if (this.ec_suppresstorustooclose === 0)
			this.ship.commsMessage("[ec_comms_torus_too_close]");
		this.ec_suppresstorustooclose++;
		if (this.ec_suppresstorustooclose>=10) 
			this.ec_suppresstorustooclose = 0;
		
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;}
		return;
	}
	if (this.ship.heading.dot(player.ship.heading) < 0.98)       // I made this a little more difficult (old value was 0.9) but it still isn't really difficult (fg, 2015-11-06)
	{
		if (ec_suppresstorusmismatch === 0)
			this.ship.commsMessage("[ec_comms_torus_heading]");
		ec_suppresstorusmismatch++;
		if (ec_suppresstorusmismatch >= 10)
			ec_suppresstorusmismatch = 0;
		
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;}
		return;
	}
	else
	{
		this.ship.reactToAIMessage ("NO_JUMP");
	}
}

this.ec_removeFrameCallback = function() // called from ec_mothergroupjumpAI.plist to reset jump synchronisation in between each check for masslock.
{
	if (this.ec_jumpCallback)
	{
		removeFrameCallback(this.ec_jumpCallback);
		delete this.ec_jumpCallback;
	}
	this.ship.velocity = this.ship.thrustVector; // reset velocity if jump has just occurred.
	player.ship.velocity = player.ship.thrustVector;
}

this.ec_matchVelocity = function() // called by framecall back whilst jump drives synchronised to match player and mother velocity.
{player.ship.velocity = this.ship.velocity;}

// called from ec_mother2AI.plist - dual function - checks for ships in scanner range that are actively hostile for player and if so sends mother into combat mode - or if none present controls decision to inject from mass lock behaviour.
this.ec_checkplayerhostilealert = function()
{
	if (player.alertHostiles)
	{
		var targets = system.filteredEntities(this, ec_checkplayerhostiles, this.ship, this.ship.scannerRange);
		if(targets.length > 0)
		{
			this.ship.target = targets[0];
			this.ship.setAI("ec_motherIntercept2AI.plist");
		}
		else
		{
			this.ec_motherfrustration++;
			this.ship.reactToAIMessage ("NO_PLAYER_HOSTILE");
		}
	}
	else if (player.alertMassLocked && this.ship.position.distanceTo(player.ship.position) < this.ship.scannerRange)
	{
		if (((Math.random()*4)+1) < this.ec_motherfrustration && player.ship.equipmentStatus("EQ_FUEL_INJECTION") === "EQUIPMENT_OK" && player.ship.fuel > 1)
		{
			this.ship.fuel = 7; 
			this.ec_maxinjectiterations = Math.round((this.ship.scannerRange*2.5)/(this.ship.maxSpeed * 7));
			this.ship.commsMessage("[ec_comms_injector]");  
			this.ec_injectcounter = 0;
			this.ship.reactToAIMessage ("INJECT");
			this.ec_motherfrustration = 0;
		}
		else
		{
			this.ec_motherfrustration++;
			this.ship.reactToAIMessage ("NO_PLAYER_HOSTILE");
		}
	}
}

// used to check for entities other than the player masslocking the mother
this.ec_checkformasslock = function(entity) 
{return ((entity.scanClass === "CLASS_NEUTRAL" || entity.scanClass === "CLASS_MILITARY" || entity.scanClass === "CLASS_POLICE" || entity.scanClass === "CLASS_THARGOID" || entity.scanClass === "CLASS_STATION") && !entity.isPlayer);}

// used to check for entities hostile to the player
this.ec_checkplayerhostiles = function(entity) 
{return (entity.isThargoid || (entity.isShip && entity.target && entity.target === player.ship && entity.hasHostileTarget && !entity.isDerelict));}

// called from ec_mother2AI.plist whilst injecting to check for entities hostile to the player.
this.ec_checkplayerhostilealertinject = function()
{
	if (player.alertHostiles)
	{
		var targets = system.filteredEntities(this, ec_checkplayerhostiles, this.ship, this.ship.scannerRange);
		if(targets.length > 0)
		{
			this.ship.target = targets[0];
			this.ship.setAI("ec_motherIntercept2AI.plist");
		}
	}
}

// called whilst mother is injecting to escape masslock to check if scanner range is now clear of masslocking entities and if so stop injectors.
this.ec_checkinjectconditions = function()
{
	var targets = system.filteredEntities(this, ec_checkformasslock, this.ship, this.ship.scannerRange);
	if((targets.length === 0 && !player.alertMassLocked) || this.ec_injectcounter > this.ec_maxinjectiterations)
		{this.ship.reactToAIMessage ("STOP_INJECT");}
	this.ec_injectcounter++;
}

// event handler driven function to define contract success.
this.shipEnteredStationAegis = function()
{
	this.ship.reactToAIMessage ("AEGIS_IN_DOCKING_RANGE");
	if (worldScripts["Escort_Contracts"].ec_currentcontract === true && system.info.systemID === worldScripts["Escort_Contracts"].ec_targetsystem && !this.ship.hasHostileTarget && !player.alertHostiles)
	{
		worldScripts["Escort_Contracts"].ec_currentcontract = "success";
		this.ship.commsMessage("[ec_comms_arrived_in_aegis]"); 
	}
	else if (worldScripts["Escort_Contracts"].ec_currentcontract === true && system.info.systemID === worldScripts["Escort_Contracts"].ec_targetsystem)
	{
		this.ship.commsMessage("[ec_comms_pirates_in_aegis]");
	}
}

// event handler driven function defining contract success.
this.shipWillDockWithStation = function(station)
{
	if(worldScripts["Escort_Contracts"].ec_currentcontract === true)
	{
		worldScripts["Escort_Contracts"].ec_currentcontract = "success";
		this.ship.commsMessage("[ec_comms_safely_docked]");
	}
}

// event handler driven function defining contract failure.
this.shipDied = function()
{   
	if (worldScripts["Escort_Contracts"].ec_currentcontract === true)
	{
		delete worldScripts["Escort_Contracts"].ec_currentcontract;
		worldScripts["Escort_Contracts"].ec_escortrep -= 2;
		if (worldScripts["Escort_Contracts"].ec_escortrep < -10)
			{worldScripts["Escort_Contracts"].ec_escortrep = -10;}
		player.commsMessage(expandDescription("[ec_comms_mother_destroyed]")); 
		player.consoleMessage("Escort Contract: Mother has been destroyed. Contract expired.", 15);
		mission.setInstructionsKey (null, "Escort_Contracts");
	}
	else 
	{
		delete worldScripts["Escort_Contracts"].ec_currentcontract;
	}
}

// only used by ec_killplayerAI.plist - if mother loses player as target she is removed.
this.ec_removemother = function()
{this.ship.remove(true);}

// player communication when player first joins mother in target system.
this.ec_commsplayerwelcome = function()
{this.ship.commsMessage("[ec_comms_arrived]");}

// player communication when mother is attacked by hostile craft.
this.ec_motherunderattack = function()
{
	if (this.ec_playerwarnings !== 5)  // if === 5, mother is attacking the player for being agressive or incompetent!
	{
	    if (this.ec_suppressunderattack === 0)
		{	
			if (this.ship.position.distanceTo(player.ship.position) < this.ship.scannerRange)
			{
				// Mother in range, warn only if the player isn't fighting already 
				if (player.alertCondition !=3)  
				{	
					this.ship.commsMessage("[ec_comms_pirate_detected]");
				}
			}   
			else
			{
				// Mother out of range, call the player in any case!
				player.commsMessage(expandDescription("[ec_comms_under_attack]"));  
			}  
		}	
		this.ec_suppressunderattack++;
		if (this.ec_suppressunderattack >= 10)     
			this.ec_suppressunderattack = 0;  
	}
}