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

Expansion UPS Courier

Content

Warnings

  1. Required Expansions mismatch between OXP Manifest and Expansion Manager at character position 0071 (DIGIT ZERO vs LATIN SMALL LETTER N)
  2. No version in dependency reference to oolite.oxp.phkb.MarketScriptInterface:null

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description This OXP adds several random missions that run simultaneously. The first set of missions offered will be various transport missions. After you have run enough of these, a series of combat jobs will be offered. Update to the original by Eric Walsh, with contributions from spara, Keeper and phkb. This OXP adds several random missions that run simultaneously. The first set of missions offered will be various transport missions. After you have run enough of these, a series of combat jobs will be offered. Update to the original by Eric Walsh, with contributions from spara, Keeper and phkb.
Identifier oolite.oxp.EricWalch.UPSCourier oolite.oxp.EricWalch.UPSCourier
Title UPS Courier UPS Courier
Category Missions Missions
Author Eric Walch, spara, Keeper, phkb, Rorschachhamster Eric Walch, spara, Keeper, phkb, Rorschachhamster
Version 2.1.2 2.1.2
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
  • oolite.oxp.phkb.MarketScriptInterface:0
  • oolite.oxp.phkb.MarketScriptInterface:
  • Optional Expansions
    Conflict Expansions
    Information URL https://wiki.alioth.net/index.php/UPS_Courier n/a
    Download URL https://wiki.alioth.net/img_auth.php/8/8e/UPS_Courier.oxz n/a
    License CC-BY-SA 3.0 CC-BY-SA 3.0
    File Size n/a
    Upload date 1697984498

    Documentation

    Also read http://wiki.alioth.net/index.php/UPS%20Courier

    readme.txt

    Universe Parcel Shipment - courier (version 2.0)
    
    An add-on for Oolite by Giles Williams.
    Based on the good old "Elite" by Ian Bell and David Braben.
    
    Intro
    =====
    In the universe there is a lot of commercial traffic. Goods and documents are transported from one solar system to the other system. Like transportation on earth, only a small number of the jobs are offered on the open market. Almost everybody can accept such free contracts provided their ship has the right cargo capacity.
    However, most of the lucrative transport jobs aren't offered on the open market. You have to use connections to gain such contracts.
    
    UPS is only one of the many organization in the universe for delivering goods between planet systems. Normally they use their own ships and pilots. On rare occasions they ask strangers to do some deliveries for them. If they do it well, the chance of getting more offers increases. Only the very skilled pilots get the best offers. UPS is present in the whole universe with business points in every solar-system with a political level of Multi-Government and higher. They don't have their own combat ships to protect their transport ships, but rely on police and occasionally on independent fighters like yourself.
    
    Earning money is hard and competition is strong in the universe. For regular transport missions UPS pays very little. If you don't find the payment enough, go to the passenger / cargo contract market - you might find a better offer there. But bear in mind: For their regular delivery pilots UPS sometimes has very good paying jobs to offer.
    
    The UPS Courier OXP
    ===================
    This OXP adds several random mission that can run simultaneously - up to 5 missions active at once. The first set of missions offered are mainly various transport missions. After you complete enough transport missions, a series of combat jobs is offered, but the main focus of the OXP stays with friendly transports. You can view these missions as more advanced versions of the Passenger/Cargo contracts that are offered via the F4 interface screens. Unlike most mission OXP's that offer a story that is only run once, this OXP will continually offer random missions, but with a low probability of appearing. You will get an end message when the last type of regular combat mission has successfully ended and nothing new is to be expected. There could still be some small variations on existing missions. The OXP itself never ends as long as it stays in the AddOns folder of Oolite.
    
    When you remove the OXP from the AddOns folder, the last settings of the mission variables stay in your saved file, doing nothing. The only thing that does not disappear is the mission destination in the long-range-galaxy chart. (red cross). If you put the OXP again in place, the missions goes on were you left them (or the missions are reset if you switched galaxies).
    
    Most missions only work as long one stays in the galaxy where the mission is accepted. On intergalactic jumps those mission are aborted as there is no solution in the new galaxy, but a new mission will be presented in the new galaxy soon enough. So intergalactic jumping in the middle of a mission should do no real harm.
    Because of the nature of this OXP you can leave it in place and forget about it. It should be compatible with most other mission OXP's.
    
    All missions start at random, but the first mission will start in a democratic system with a 20% chance. All missions are rare, so you better focus on something else and let yourself be surprised by the offers. During the game it is always safe to refuse a mission if you have better things to do. The mission, or a similar one, will be offered again later on. Most missions have a counter which can come into play where new types of missions are only available after you have completed a certain number of the old ones.
    
    Some missions have a time limit, others only claim that there is urgency but the time only starts on entering the target system. If there is a time limit, the Oolite clock will be used. Be aware that hyperspace jumps take more time if the jump is over a longer distance. So jumping a certain distance in one big jump takes significantly more oolite-time than traveling the same distance with two smaller jumps. 
    
    Transport reputation
    --------------------
    Couriers with a good reputation are more likely to get an offer for a new mission. For transporting documents you don't need a good reputation but you do need at least a good reputation to get parcel transport offers. The variable to keep track of the reputation is the same as that used by Oolite itself for the built-in transport reputation. So you can also increase your reputation by taking these transport missions. It is even advisable to do some regular cargo transports frequently because this reputation erodes with time, so completing transport missions will maintain your reputation. Your current reputation is also dependent on the system you are in and will be shown at the F5/F5 screen.
    
    Other OXP's
    -----------
    This oxp checks for "Transports.OXP" or "Dictators.OXP" being installed. When a version of those is present, UPS couriers uses some of these ships instead of generic ships. It is not necessary to have them installed.
    
    Performance
    -----------
    Because it is intended that this OXP always stays active, great effort is taken to reduce the amount of computing. It will only do its necessary computing when missions are busy. It will add no additional ships when no mission is active. Later on in the game, some custom ships may appear without a mission active, but these ships are selected by the system instead of a default ship and don't add additional ships to a system. Controlling files of custom ships take no more calculation power than standard ships. So from performance point of view, there is no reason to remove this OXP with other OXP's active.
    
    Mission groups
    ==============
    Document missions: Several missions involving document transportation.
    Parcel missions: Several missions involving parcel transportation.
    Container missions: Several missions involving combat.
    Solar missions: Several missions around a solar station.
    Slave missions: Several missions involving rescuing slaves.
    
    Some of these missions are triggered by parts of other mission groups. All mission groups have their own mission descriptions and can run simultaneously. Within a mission group there can only be one mission active.
    
    Version requirements
    ====================
    This OXP runs in every galaxy and with every combat rating. However, with higher combat ratings the chances rise to get more opponents in combat.
    UPS version 2.0 requires the features added with Oolite 1.82.
    
    Eric Walch,
    Raalte, the Netherlands
    
    Former player of Elite on a Commodore 64 in another universe and another century.
    
    I can be contacted as member at: http://aegidian.org/bb
    Information and discussion about the original OXP (pre v1.9) can be found on: http://oolite.aegidian.org/bb/viewtopic.php?f=4&t=3814
    Information and discussion about spara's 1.9 version can be found on: http://aegidian.org/bb/viewtopic.php?f=4&t=16379
    Information and discussion about the newer OXP (2.0ff) can be found on: http://aegidian.org/bb/viewtopic.php?f=4&t=18520
    The latest version is available at the Wiki page: http://wiki.alioth.net/index.php/UPS_Courier
    
    Copyright notes
    Copyright 2008-2013 by Eric Walch
    UPS Courier is distributed under the Creative Commons Attribution-Noncommercial-Share Alike license, version 3.0 or later.
    Please visit http://creativecommons.org/licenses/by-nc-sa/3.0/ for more info.
    
    Version history
    1.0     Initial release with document, parcel, container and solar missions.
    1.1     Added restart of trumble-mission.
            Added slave mission. (this mission needs oolite version 1.65 or newer to be offered)
            Made sure no ships are unintended placed in witchspace during misjump.
    1.1.1   Added one new mission to the slavemissions.
    1.1.2   Fixed unwanted ship launching from special stations under oolite 1.69
    1.1.3   Added a non-combat mission in the container series.
            Increased minimum Oolite version to 1.63 to allow for consoleMessages.
            Distinguishing between player and NPC scooping of scripted cargopods.
            Fixed bug in trumble restart not always working.
    1.2     Added a mining mission in the document series.
            Changed the behaviour of the ships in the slave mission that was added in version 1.1.1
            Made some opponents stronger after several encounters.
            Added two pieces of equipment buyable by the player. For the private escorts you must have scored a lot of mission points. 
            The other is a scanner and only comes available to elite players that completed the last mission for at least the second time. 
            Just two fun things to waist your money on.
    1.2.1   Added the "no_boulder" key that was introduced in Oolite 1.70
            Restructuring of the code for speed performance. As little as possible conditions are evaluated at idle time.
            Removed a bug introduced in version 1.1.3 that certain missions in the container series prevented from happening.
    1.2.2   UPS deliveries now also count for the overall players transport reputation.
            Bugfix in the 2nd container mission. But was introduced in version 1.2.1
    1.2.3   AI improvements off several ships and introduced java scripting for some ships. 
            (they fall back on the legacy scripting when running on a pre 1.70 Oolite version)
    1.3     Start implementation of Java Script. All new things in following versions will only be available in JS. Meaning on oolite 1.70 or newer.
    1.3.0   The slave- and solar missions will use Java Script when Oolite 1.70 or newer is used. Under Oolite 1.65 the old Legacy scripting is used.
    1.3.1   Translation of the parcel missions into Java script.
    1.3.2   Translation of the docs missions into Java script.
    1.3.3   Translation of the container missions into Java script. Added timers to mission offerings for multiple offerings on one station.
    1.3.4   Added Java scripts to some ships for a rare mid-air encounter.
    1.3.5   Added option to look at long range chart first, before accepting a mission.
    1.3.6   The JS version of the "private Escort" now works. 
            Added rotating ships on some missions (added this also to the legacy version). 
            Added celebration messages when a main target was destroyed.
    1.3.7   Added ups barrels to the game. Used for missions but also in used in the general barrel pool. 
            Removed a bug in the docs series that inhibited  doc-mission offers.
    1.3.8   Bugfix of transferring solar mission
    
    1.4.0   First release with oolite 1.71 adoptions.
    1.4.1   Bugfixes
    1.4.2   Bugfix and 1.72 preparations. Changed several scripted containers into normal containers now script added normal containers work well with 1.71
    1.4.3   Added better counter measures against all new types of oxp added missiles for the final mission. 
            Removed bug (introduced in 1.4.2) with boa mission. Removed some other minor bugs.
    1.4.4   Hired escorts now can attack a hostile player target and not just pirates. Doubled the cost for hiring escorts.
    
    1.5.0   Removed any compatibility with older Oolite versions.
            Raised minimum Oolite version to 1.72. Reinstalled the "long range chart looking" for some mission offers.
            Made some parcel missions dependent on contract reputation. One now needs a positive reputation to get a parcel offer.
    1.5.1   Added two new transport missions after suggestion of Pagroove: Tionisla chronicle delivery and medicine delivery. 
            Fixed a situation were the player jumps to another galaxy when ups was not installed. 
            UPS will now reset missions properly after re-instalation of the oxp in an other galaxy. 
            Fixed several occasions of to much spam messages by mission pirates. Spam messages are now just not sent when the last was very recent. 
            Raised minimum version to 1.72.1 (1.72.0 will crash on a certain specific command I need for the new mission)
    1.5.2   Changed bounty of the Boa mission. Ship should now get less chased by police.
    1.5.3   Added extra code for the Boa mission. Because of an Oolite timer bug, version 1.5.2 could crash oolite during this Boa mission.
    1.5.4   Fixed bug with private escorts. Bug was introduced in version 1.5.0
    1.5.5   Added time left in days&hours for two mission descriptions.
    1.5.6   Removed bug in Python mission. (Bug added with 1.5.5)
    1.6.0   Raised minimum Oolite version to 1.73
    1.6.1   Changed maximum version to 1.73.x (will be incompatible with 1.74)
    1.7.0   Implemented the new mission offering code introduced with Oolite 1.74. Minimum version is now 1.74.
            Added one new mission that makes use of the new possibility to read player cargo.
    1.7.1   Fixed a problem with inconsistent casing that gives problems on Lunux systems.
    1.7.3   Fixed a casing error with JS. This error was accepted until Oolite 1.74.2 but will generate errors in later versions.
    1.7.4   Added a 'null' parameter to setInstructionsKey() to avoid warnings after Oolite 1.74.2
    1.7.5   Found another casing error with "System".
    1.7.6   Found another casing error with "Manifest". (thanks Okti)
    1.7.7   Fixed a problem of some ships not being added at the intended location with Oolite 1.75.
    1.8.0   Raised minimum Oolite to 1.75.1 Added randomized pilots to some ships. (Unreleased yet)
    1.8.1   Fixed a bug from version 1.8.0 were some missions did not start or finish.
    1.8.2   Fixed an equipment bug that accidentally removed the cloak from the player in pirate-rock mission. 
            Bug was already present in the very first version 1.0, but only blows into your face when using Oolite 1.77 or newer.
    1.8.3   Fixed a few text expansions with DockedStationName_string.
            Fixed a bug with the reward in the Python mission.
    1.8.4   Fixed a bug from the 1.8.3 version.
    1.9     Update by spara
            For compatibility with various shipsets, all references to core models have been removed and replaced with like_ships.
            Ships
                For compatibility, removed special exhausts
                Replaced oxp specific boas and python with like_ships
                Unified ship naming overall and updated to use randomshipnames oxp, if present.
                Updated escorts to use escort formations oxp, if present
            Asteroids
                No more custom asteroids in missions. Uses any available asteroids now.
                Special splinter models are now re-textured Griff models. Corrected to work with Ore Processor's scan function.
            Stations
                Solar research station is replaced with a turretless Kiota Solar station, if present.
                Pirate bases are replaced with Griff's hermit, if present
            Other
                Fixed black box to work correctly with Griff's barrel
                Metal plates normal mapped by Keeper
                Ground radars enhanced with normal & emission maps
                Fixed ground radars from being stations with docking possibilities
    2.0     Update by phkb
            Updates to ship/container models to better fit with the Griff models post-Oolite 1.80.
            Switched Black Box to be the same as cim's Black box from Rescue Stations.
            Added screenID's to all mission screens.
            Update to UPS mission screen overlay image.
            Spelling/grammar corrections in missiontext.plist and descriptions.plist.
            Update of market data to work with newer system post-Oolite 1.82.
            Converted readme.rtf to txt file.
            Bug fixes.
    2.0.1   Update by phkb
            Removed legacy script that added AstroFactories after launching.
            Removed code relating to pre-1.77 versions of Oolite. Minimum version now 1.82.
            Fixed Javascript error in slaves market adjustment code.
    2.0.2   Added new texture for Solar Researcher, provided by Keeper.
            Added role-categories.plist file to replace the "pirate-victim-roles.plist" file.
            Converted demoships.plist file to OpenStep format.
    2.0.3   Really fixed errors in slave market adjustment code.
    2.0.4   Added code to prevent missions to unreachable destinations.
            Displaying a mission destination on a system map will no longer reset the current hyperspace destination.
            Small adjustments to missiontext items.
    2.0.5   Small changes to some beacon labels in shipdata.plist.
            Added additional entries to save game data.
    2.0.6   Changed ship naming convention used on Solar Shuttles.
            Hopefully stopped issue of mission completion screens being displayed more than once.
    2.0.7   Added chart marker cleanup routine.
    2.0.8   Fixed issue with Sun Stations not being recognised as dockable stations.
    2.1.0   Changed slave satellite radar to be a variant of Rorschachhamster's spy satellite.
            Added missing dock to ground radar definition to fix communications between radars. 
            Made Snoopers optional, and made use of the new GNN OXP (if installed).
            Mission text updates
            Spelling/grammar corrections.
    2.1.1   Small correction to a comms message in the fuel leak AI.
    2.1.2   Fixes for improved integration with the Bounty System OXP.
            Made compatible with MarketScriptInterface.oxp.
    

    Equipment

    Name Visible Cost [deci-credits] Tech-Level
    Cloaking Device yes 1500 1+
    Pirate ID Cleaner yes 20000 1+
    GalCop Scanner yes 30000 1+
    Private Escort no 42000 1+
    Universal Scanner yes 800000 1+
    Remove Universal Scanner yes 200 14+

    Ships

    Name
    anaconda-derelict2_ptt
    anaconda-derelict_ptt
    anaconda_ptt
    asp_ambush_escort
    Asp Mark IV
    asp_ptt
    Black Box
    boa-mk2_ptt
    boa2_ptt
    boa3_ptt
    boa4_ptt
    boa_ptt
    krait_ptt
    Pirate Rock
    Pirate Rock
    pirate-upsbase2-core
    pirate-upsbase2-griff
    pirate-upscapsule
    python-derelict_ptt
    python-scavenger-ups
    python2_ptt
    python3_ptt
    python_ptt
    sunbase-upscapsule
    Cargo container
    Boa Class Cruiser
    Boa
    Metal fragment
    ups-alloy2
    ups-alloy3
    ups-asp-escort
    Cargo container
    ups-barrel1
    Cargo container
    ups-barrel3
    ups-barrel4
    ups-barrel5
    ups-barrel6
    ups-barrel7
    ups-barrel8
    Black Box
    Solar Navigation Buoy
    ups-cobra-escort
    ups-cobra3
    ups-cobra3-miner
    UPS container
    Sun Research Station Alpha
    Sun Research Station Alpha
    Sun Research Station Beta
    Sun Research Station Beta
    Sun Research Station Omega
    Sun Research Station Omega
    Sun Research Station Delta
    Sun Research Station Delta
    No-Dock dock
    ups-ferdelance-l
    ups-fuelship1
    ups-fuelship2
    ups-gecko-escort
    ups-satellite-asp
    ups-satellite-cobra
    ups-satellite-cobra3
    ups-satellite-gecko
    ups-satellite-moray
    ups-slave-convoy-f
    ups-slave-convoy-l
    Ground Radar
    ups-slave-shuttle
    ups-slave_convoy_barrel
    ups-slavebarrel
    ups-slavecobra
    Solar Research Ship
    Solar Research Ship II
    Corona Research Shuttle
    Surface Research Shuttle
    ups-splinter1
    ups-splinter2
    ups-splinter3
    ups-splinter4
    ups-splinter5
    ups-splinter6
    ups-splinter7
    ups-splinter8
    ups-sun-anaconda
    ups-sun-boa-mk2
    ups-sun-python-trader
    ups-sun-viper
    ups-sun2-anaconda
    ups-sun2-python-trader
    ups-viper-escort
    ups-viper-escort2

    Models

    This expansion declares no models. This may be related to warnings.

    Scripts

    Path
    Scripts/demoScript.js
    "use strict";
    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.74.
    
    
    // startUp only runs once at startup before the demoscreen shows up and after loading in a saved game.
    this.startUp = 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){
        /*
        This is the place to add your messages to the arrival report with:
        if(condition) player.addMessageToArrivalReport(expandDescription("Your message"));
        */
    };
    
    this.shipDockedWithStation = function (station){
        // do your stuff here.
    };
    
    this.missionScreenOpportunity = function (){
        /*
        This handler triggers:
        - After loading a saved game.
        - After docking.
        - After a missionscreen ended.
        It will only trigger for one oxp at a time and stop triggering when an oxp takes the Opportunity.
        There is no need to do additional tests: when an opportunity is given, it is safe to display a missioncreen.
        */
        if (!player.ship.docked) return;
        this.missionOffers();
    };
    
    this.missionOffers = function (){
        if (player.ship.dockedStation.isMainStation) {
            //  Your stuff for the main station
            /*
            Missionscreens are set up using the command:
            mission.runScreen({title: "", messageKey: "", etc...}, this.choiceEvaluation)
    
            When you do an offer it is often advisable to set a variable to tell an offer is made. (e.g. this.offering)
            This prefents comming in a loop of offering. There is no need to use a missionVarriable for this,
            a "this.Foo" will do. But always remember that a player can launch without making a selection, so
            you must clear such variables on launch.
            */
        } else if (player.ship.dockedStation.shipDescription == "My Station"){
            // Your stuff for a special named station
        }
    };
    
    this.choiceEvaluation = function (choice){
        if (this.offering === "MY_OFFER"){
             /*
             the choise is only offered to the oxp that created the page, so there is no longer the risk
             of other oxp reacting on a chosen answer.
             */
            if (choice === "YES_MISSION"){
                // put your code here.
            }else{
                if (choice === "NO_MISSION"){
                    // put your code here
                }
            }
        }
        this.offering = null;
    };
    
    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}
       this.offering = null; // in case a ship launched from within a missionscreen without making a choice.
        // put here your other code.
    };
    Scripts/upsAsteroid.js
    "use strict";
    this.name        = "upsAsteroid";
    this.author      = "spara";
    this.copyright   = "2014 Mika Spara";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "Code for special asteroids/boulders/splinters";
    
    //add position marker when asteroid/boulder with this ship_script is destroyed to script the spawned debris.
    this.shipDied = function() {
    	system.addVisualEffect("ups_position_marker", this.ship.position);
        if (worldScripts["ups_docs"].ups_docs === "ASTEROID" && ship.$upsAsteroid)
        {
            // doing the asteroid mission
            missionVariables.ups_asteroid_count-- ;
        }
    }
    
    //marker has been spawned, wait for 0.1 seconds so that the game has time to spawn boulders/splinters
    this.effectSpawned = function() {
    	this.debrisTimer = new Timer(this, this.$scanDebris, 0.1);
    }
    
    //scan for boulders/splinters and script accordingly
    this.$scanDebris = function() {
    	//if there are asteroids/boulders close by, assume that they are products of just destroyed asteroid and script them. this method also works with asteroids from asteroid storm oxp and griffs rock chunks.
    	var rocks = system.entitiesWithScanClass("CLASS_ROCK", this.visualEffect, 1000);
    	for (var i = 0; i < rocks.length; i++) {
    		rocks[i].setScript("upsAsteroid.js");
    	}
    	//if there are splinters close by, assume that they are products of just destroyed boulder and add valuables to them.
    	var splinters = system.shipsWithPrimaryRole("splinter", this.visualEffect, 1000)
    
    	for (var i = 0; i < splinters.length; i++) {
    		if (splinters[i].commodity === "minerals") {
    			var position = splinters[i].position;
    			splinters[i].explode();
    			splinters[i] = system.addShips("ups-splinter", 1, position, 0)[0];
    			splinters[i].temperature = 0.99;
    			splinters[i].primaryRole = "splinter";
    			var amount = Math.ceil(Math.random() * 12);
    			var metal = Math.random() < 0.40 ? "platinum" : "gold";
    			splinters[i].setCargo(metal, amount);
    			//make the splinters to be identified with the ore processors scanning function correctly
    			splinters[i].$oreProcessor = {
    				type: metal,
    				quantity: amount
    			};
    		}
    	}
    	this.visualEffect.remove();
    }
    
    Scripts/upsBlackBox.js
    "use strict";
    this.name		 = "upsBlackBox";
    this.author		 = "eric Walch";
    this.copyright   = "2008 eric walch.";
    this.license     = "CC BY-NC-SA 3.0";
    this.comment	 = "Script for the ups-black box sun mission"
    
    this.shipWasScooped = function (scooper)
    {
    	 if (scooper.isPlayer)
    	 {
    		worldScripts["ups_sun"].ups_s2count++;
    		worldScripts["ups_sun"].ups_s2award += 250;
    	 }
    }
    
    Scripts/upsBoa.js
    "use strict";
    this.name      = "upsBoa";
    this.author    = "eric walch, spara";
    this.copyright = "2008 eric walch. 2014 spara";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.oldStatus = 100; // real ship bounty
    	this.checkPlayerTimer = new Timer(this, this.checkPlayer, 20, 10);
    	//name escorts with randomshipnames, if present -spara-
    	if (worldScripts.randomshipnames && this.ship.escorts) {
    		for (var i = 0; i < this.ship.escorts.length; i++) {
    			var shipName = worldScripts.randomshipnames.$randomHunterName(ship);
    			this.ship.escorts[i].displayName = this.ship.escorts[i].displayName + ": " + shipName;
    		}
    	}
    	//order escorts with escort formations, if present -spara-
    	if (worldScripts["Escort Formations Randomiser"])
    		worldScripts["Escort Formations Randomiser"].$setupDefensiveEscorts(this.ship);
        delete this.shipSpawned;
    }
    
    this.shipDied = function (whom, why)
    {
       log("UPS-Courier", "Hostile BOA killed by " + whom + " by means of " + why);
       if (whom && whom.isPlayer && !player.ship.isCloaked)
       {
          missionVariables.ups_closecontact = "YES"; // Killer was the player
       }
       else
       {
          missionVariables.ups_closecontact = "NO"; // Killer was an NPC or player was cloaked.
       }
        this.ship.commsMessage("I'll be back with help for revenge!!");
        this.ship.spawn("ups-spilled-container", 5);
        this.ship.spawn("ups-textilecontainer", 15);
        worldScripts["ups_container"].ups_container_count = 0;
    	worldScripts["ups_container"].ups_total_container_count = 0;
    	worldScripts["ups_container"].ups_container_award = 0;
        worldScripts["ups_container"].ups_container = "FOUND";
        worldScripts["ups_container"].upsUnmarkSystem(missionVariables.ups_cplanet, "ups_container");
        mission.setInstructionsKey("ups_containers_small1c", "ups_container");
    	if (this.checkPlayerTimer) {this.checkPlayerTimer.stop(); delete this.checkPlayerTimer};
        delete this.shipDied;
    }
    
    this.shipBeingAttacked = function (whom)
    {
    	if (this.ship.escorts && Math.random() > 0.7) this.ship.deployEscorts();
    	if (!whom.isPlayer && player.ship.position && player.ship.position.distanceTo(this.ship) > 20E3) whom.reactToAIMessage("TARGET_LOST");
    }
    
    // is only activated on real dockings. Sometimes ships are also removed from the docking queue on player launch.
    this.shipDockedWithStation = function (station)
    {
        log("UPS-Courier", "Hostile BOA docked at station: "+ station.name);
        mission.setInstructionsKey("ups_container_boa_docked_small", "ups_container");
    	missionVariables.ups_closecontact = "DOCKED";
    	if (this.checkPlayerTimer) {this.checkPlayerTimer.stop(); delete this.checkPlayerTimer};
    }
    
    this.entityDestroyed = function ()
    {
        if (this.checkPlayerTimer) {this.checkPlayerTimer.stop(); delete this.checkPlayerTimer};
    }
    
    this.sendAwayOffender = function ()
    {
    	if (!this.ship.target.isPlayer && player.ship.position && player.ship.position.distanceTo(this.ship) > 20E3) this.ship.target.reactToAIMessage("TARGET_LOST");
    }
    
    // Setting clean on/off is only to draw no police attention when there is no player nearby.
    this.checkPlayer = function ()
    {
    	if (this.ship && !this.ship.isValid)
    	{
    		log(this.name, "ERROR detected, this.ship is undefined"); // Oolite bug
    		if (this.checkPlayerTimer) {this.checkPlayerTimer.stop(); delete this.checkPlayerTimer};
    		if (this.checkPlayer) delete this.checkPlayer;
    		return;
    	}
    	if (player.ship.position && player.ship.position.distanceTo(this.ship) > 30E3) this.setClean()
    	else this.setFugitive();
    }
    
    this.setClean = function ()
    {
        if (this.oldStatus == 0)
        {
            this.oldStatus = this.ship.bounty;
            this.ship.bounty = 0;
    		if (this.ship.escorts) this.setEscortBounty(0);
        }
    }
    
    this.setFugitive = function ()
    {
        if (this.oldStatus > 0)
        {
            this.ship.bounty = this.oldStatus;
            this.oldStatus = 0;
    		if (this.ship.escorts) this.setEscortBounty(30);
        }
    }
    
    this.setEscortBounty = function (bounty)
    {
    	var escorts = this.ship.escorts;
        for (var i=0; i<escorts.length; i++)
    	{
    		if (escorts[i].AIState == "FLYING_ESCORT")
            {
                escorts[i].bounty = Math.round(Math.random()*bounty + bounty/10);
            }
    	}
    }
    
    Scripts/upsBoaPilot.js
    "use strict";
    this.name      = "upsBoaPilot";
    this.author    = "eric walch";
    this.copyright = "2010 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.unloadCharacter = function ()
    {
        player.addMessageToArrivalReport(expandMissionText("eric_thanks"))
        player.credits +=150;
        missionVariables.ups_ecount++;
    }
    
    Scripts/upsDefender.js
    "use strict";
    this.name           = "upsDefender";
    this.author         = "Eric Walch";
    this.copyright      = "2008 eric walch";
    this.description    = "1.71 integration";
    this.license        = "CC BY-NC-SA 3.0";
    
    this.neutralyseMine = function ()
    {
        if (this.ship.target && this.ship.target.AI != "upsTimebombAI.plist")
        {
            this.ship.target.switchAI("upsTimebombAI.plist");
            this.ship.commsMessage("It's my mine");
            this.ship.fireECM();
        }
    }
    
    this.neutralyseMissile = function ()
    {
        if (this.ship.target && this.ship.target.AI != "upsTimebombAI.plist" && this.ship.target.owner && this.ship.target.owner.isPlayer)
        {
            this.ship.target.switchAI("upsTimebombAI.plist");
            this.ship.commsMessage("It's my missile");
            this.ship.fireECM();
        }
    }
    
    Scripts/upsDependance.js
    "use strict";
    
    this.name      = "ups_sun_station_alpha";
    this.author    = "eric walch, spara";
    this.copyright = "2008 eric walch. 2014 spara";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.playerArrived = false;
    
    this.shipSpawned = function ()
    {
        if (worldScripts["ups_sun"].ups_sun === "RESCUE" || worldScripts["ups_sun"].ups_sun === "EXPLOSION")
        {
            this.ship.switchAI("upsSunStationFinalAI.plist"); // switch AI script on final mission.
            this.ship.requiresDockingClearance = false;
        }
    
        //don't use buoy, if station is Kiota station -spara-
        if (!ship.hasRole("ups_kiota")) {
    		var targetVector = system.sun.position.subtract(this.ship.position).direction();
    		this.ship.orientation = targetVector.rotationTo(new Vector3D(0,0,1));
    		this.buoy =  (this.ship.primaryRole == "ups_dependance") ? "ups-sun-buoy" : "ups-sun-buoy2";
    		system.addShips(this.buoy, 1, this.ship.position.add(this.ship.heading.multiply(10E3)), 1);
        }
        //remove turrets from Kiota solar station -spara-
        var subEnts = ship.subEntities;
        for (var i = 0; i < subEnts.length; i++) {
    		if (subEnts[i].primaryRole === "wildShips_kiotaTurret")
    			subEnts[i].remove();
    	}
    
        delete this.shipSpawned;
    }
    
    this.shipDied = function ()
    {
        if (worldScripts["ups_sun"].ups_sun === "RESCUE" || worldScripts["ups_sun"].ups_sun === "EXPLOSION")
        {
            log(this.name, "Station position is: "+this.ship.position);
            var escapees = system.addShips("sunbase-upscapsule", 15, this.ship.position, 500);
            for (var i=0; i<escapees.length; i++)
            {
                // escapees[i].position = this.ship.position.add(Vector3D.random().multiply(500));
                // escapees[i].velocity = escapees[i].position.subtract(this.ship.position);
                // log(this.name, "escapees position nr: "+i+" is: "+escapees[i].position);
            }
            worldScripts["ups_sun"].ups_rescue = "YES";
            worldScripts["ups_sun"].ups_sun = "EXPLOSION";
            delete this.shipDied;
        }
    }
    
    this.entityDestroyed = function ()
    {
    	if (this.heatTimer) {this.heatTimer.stop(); delete this.heatTimer;};
    }
    
    this.takeHeatDamage = function ()
    {
         if (!this.playerArrived)
         {
            this.playerArrived = true;
            this.heatTimer = new Timer(this, this.increaseHeat, 1, 1);
            this.ship.commsMessage("Help us evacuate our last soles.");
         }
         else
    	 {
    		var temp = Math.round(100 - 60 * this.ship.energy / this.ship.maxEnergy);
    		this.ship.commsMessage("Hurry up, station core temperature is at "+temp+" degr C and still rising.");
    	 }
    }
    
    this.increaseHeat = function ()
    {
        this.ship.temperature += 0.01;
        if (this.ship.temperature >= 1) {this.heatTimer.stop(); delete this.heatTimer};
    }
    
    this.launchMainstation = function ()
    {
        var main = system.mainStation;
        if (main) main.launchShipWithRole("sunskim-trader");
    }
    
    Scripts/upsDerelict.js
    "use strict";
    this.name      = "upsderelict";
    this.author    = "eric walch, spara";
    this.copyright = "2008 eric walch. 2014 spara";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.collisionDistance = this.ship.collisionRadius + player.ship.collisionRadius;
        this.shipName = this.ship.displayName;
        this.countdown = 5;
        this.maxFuel = 1;
    
    	//name escorts with randomshipnames, if present -spara-
    	if (worldScripts.randomshipnames && this.ship.escorts) {
    		for (var i = 0; i < this.ship.escorts.length; i++) {
    			var shipName = worldScripts.randomshipnames.$randomHunterName(ship);
    			this.ship.escorts[i].displayName = this.ship.escorts[i].displayName + ": " + shipName;
    		}
    	}
    	//order escorts with escort formations, if present -spara-
    	if (worldScripts["Escort Formations Randomiser"])
    		worldScripts["Escort Formations Randomiser"].$setupDefensiveEscorts(this.ship);
    
        delete this.shipSpawned;
    }
    
    this.checkFuel = function ()
    {
        if(player.ship.fuel > 0)
        {
            this.ship.commsMessage("Help anyone, we are out of fuel.");
        }
        else
        {
            this.ship.switchAI("route1TraderAI.plist");
        }
    }
    
    this.checkDistance = function ()
    {
      var distance = Math.round(player.ship.position.distanceTo(this.ship));
      if (distance < 6000)
      {
          player.consoleMessage("You are on final approach, lower your speed", 9);
          if (!worldScripts.randomshipnames)
    		this.ship.displayName = this.shipName + " without fuel";
          this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
      }
    }
    
    this.guideIn = function ()
    {
        var distance = Math.round(player.ship.position.distanceTo(this.ship) - this.collisionDistance);
        if (distance < 0) distance = 0;
        var speed = Math.round(player.ship.speed);
        if (distance > 7000)
        {
            this.ship.reactToAIMessage("UPS_PLAYER_LEAVING");
            return;
        }
        if (distance > 1500)
        {
            if(speed > 100) player.consoleMessage("Approaching with: " + speed + "km/h, lower your speed.", 4);
        }
        else
        {
            if(speed > 50) player.consoleMessage("Approaching with: " + speed + "km/h, lower your speed to 50 km/h.", 4);
            player.consoleMessage("You are at " + distance + " meters, come to a complete standstill within 100 meters.", 4);
        }
        if(distance < 100 && speed == 0)
        {
            this.ship.reactToAIMessage("UPS_PLAYER_CLOSE");
            this.oldOrientation = player.ship.orientation;
        }
    }
    
    this.checkSpeed = function ()
    {
      if (player.ship.speed > 0.001 || this.oldOrientation.dot(player.ship.orientation) < 0.999)
      {
        player.consoleMessage("You are moving to much, hold still", 4);
        this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
        this.countdown = 5;
      }
      else if(this.ship.speed > 0)
      {
        player.consoleMessage("That was to close", 4);
        this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
        this.countdown = 5;
      }
      else
      {
        player.consoleMessage("Transfering fuel..... " +this.countdown, 3);
        if(player.ship.fuel > 0 && this.maxFuel > 0)
        {
            player.ship.fuel -= 0.2;
            this.maxFuel -= 0.2;
        }
        this.countdown--;
        if(this.countdown<0) this.ship.reactToAIMessage("UPS_TRANSFERING");
      }
    }
    
    this.thankPlayer = function ()
    {
        if(player.ship.fuel > 0)
        {
            this.ship.commsMessage(expandDescription("Thank you for the fuel [commander_shipname]."));
        }
        else
        {
            this.ship.commsMessage(expandDescription("Thank you for giving your last bit of fuel [commander_shipname]."));
        }
        if (!worldScripts.randomshipnames)
    		this.ship.displayName = this.shipName;
    }
    
    this.tranferPayment = function ()
    {
        player.ship.manifest["gem_stones"] += 50;
        missionVariables.ups_tcount++;
    }
    
    this.subtractAllFuel = function ()
    {
        this.ship.commsMessage(expandDescription("Thank you for giving your last bit of fuel [commander_shipname]."));
        player.ship.fuel = 0;
        if (!worldScripts.randomshipnames)
    		this.ship.displayName = this.shipName;
        this.ship.bounty += 50;
        if(this.ship.escorts.length>0)
        {
            for(let i=0; i<this.ship.escorts.length;i++)
            {
                if(this.ship.escorts[i].isValid) this.ship.escorts[i].bounty += 25;
            }
        }
    }
    
    this.shipExitedWormhole = function ()
    {
    	this.ship.primaryRole = "trader";
    }
    
    Scripts/upsEscorts.js
    "use strict";
    this.name      = "upsescorts";
    this.author    = "spara";
    this.copyright = "2014 spara";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
    	//name escorts with randomshipnames, if present -spara-
    	if (worldScripts.randomshipnames && this.ship.escorts) {
    		for (var i = 0; i < this.ship.escorts.length; i++) {
    			var shipName = worldScripts.randomshipnames.$randomHunterName(ship);
    			this.ship.escorts[i].displayName = this.ship.escorts[i].displayName + ": " + shipName;
    		}
    	}
    	//order escorts with escort formations, if present -spara-
    	if (worldScripts["Escort Formations Randomiser"])
    		worldScripts["Escort Formations Randomiser"].$setupDefensiveEscorts(this.ship);
    
        delete this.shipSpawned;
    }
    
    Scripts/upsFuelLeak.js
    "use strict";
    this.name      = "fuelLeakUps";
    this.author    = "eric walch";
    this.copyright = "2008 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.collisionDistance = this.ship.collisionRadius + player.ship.collisionRadius;
        this.shipName = this.ship.displayName;
        delete this.shipSpawned;
    }
    
    this.shipDied = function (whom, why)
    {
        if(this.ship.subEntities && this.ship.subEntities.length > 0) // ship still has subentities.
        {
            for (var i=0; i<this.ship.subEntities.length; i++)
            {
                var silo = this.ship.spawnOne(this.ship.subEntities[i].primaryRole);
                var siloPosition = this.ship.subEntities[i].position;
                siloPosition = siloPosition.multiply(3);
                siloPosition = this.localToGlobal(siloPosition);
                if (silo && silo.isValid)
                {
                    silo.orientation = this.ship.orientation;
                    silo.position = siloPosition;
                }
            }
        }
    }
    
    this.localToGlobal = function (position)
    {
    	return this.ship.position.add(position.rotateBy(this.ship.orientation));
    }
    
    this.payment = 0;
    
    this.checkLocation = function ()
    {
      if (system.isInterstellarSpace)
      {
        player.consoleMessage("I'll give you fuel for " + 1500 * (7 - player.ship.fuel) + " credits.", 9);
        this.amount = 1500;
      }
      else
      {
        player.consoleMessage("This entry point iS unsafe and causes fuel leaks.", 9);
        this.amount = 150;
        player.ship.fuelLeakRate = 20;
      }
    }
    
    this.checkDistance = function ()
    {
      var distance = Math.round(player.ship.position.distanceTo(this.ship));
      if (distance < 6000)
      {
          player.consoleMessage("You are on approach, lower your speed", 9);
          this.ship.displayName = this.shipName + " with fuel";
          this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
      }
    }
    
    this.guideIn = function ()
    {
        var distance = Math.round(player.ship.position.distanceTo(this.ship) - this.collisionDistance);
        if (distance < 0) distance = 0;
        var speed = Math.round(player.ship.speed);
        if (distance > 7000)
        {
            this.ship.reactToAIMessage("UPS_PLAYER_LEAVING");
            return;
        }
        if (distance > 1500)
        {
            if (speed > 100) player.consoleMessage("Approaching with: " + speed + "km/h, lower your speed.", 4);
        }
        else
        {
            if (speed > 50) player.consoleMessage("Approaching with: " + speed + "km/h, lower your speed to 50 km/h.", 4);
            player.consoleMessage("You are at " + distance + " meters, come to a complete standstill within 100 meters.", 4);
        }
        if (distance < 100 && speed == 0)
        {
            this.ship.reactToAIMessage("UPS_PLAYER_CLOSE");
            this.oldOrientation = player.ship.orientation;
        }
    }
    
    this.checkSpeed = function ()
    {
        if (player.ship.speed > 0.001 || this.oldOrientation.dot(player.ship.orientation) < 0.999)
        {
            player.consoleMessage("You are moving to much, hold stil", 4);
            this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
        }
        else
        {
            if (player.ship.fuel < 7 && player.credits > this.amount)
            {
                player.consoleMessage("Transfering fuel... " + Math.round(player.ship.fuel), 3);
                player.ship.fuel += 1;
                this.payment += this.amount;
                player.credits -= this.amount;
            }
            else
            {
                if (this.payment === 0)
                {
                    player.commsMessage("No money no fuel. I can't help you", 6);
                }
                else
                {
                    player.commsMessage("I took " + this.payment + " credits as payment.",6)
                    this.ship.reactToAIMessage("UPS_TRANSFERING_OK");
                    this.ship.target = null;
                }
            }
            this.ship.displayName = this.shipName; // restore original name
        }
    }
    
    Scripts/upsMarket_dependance.js
    "use strict";
    this.name      = "upsMarket_dependance";
    this.author    = "spara";
    this.copyright = "2014 spara";
    this.license   = "CC BY-NC-SA 4.0";
    
    this.$originalDefs = {
        "food": [0, 0, 19, -2, -2, 6, 1, 1, 0],
        "textiles": [0, 0, 20, -1, -1, 10, 3, 3, 0],
        "radioactives": [0, 0, 65, -3, -3, 2, 7, 7, 0],
        "slaves": [0, 0, 40, -5, -5, 226, 31, 31, 0],
        "liquor_wines": [0, 0, 83, -5, -5, 251, 15, 15, 0],
        "luxuries": [0, 0, 196, 8, 8, 54, 3, 3, 0],
        "narcotics": [0, 0, 235, 29, 29, 8, 120, 120, 0],
        "computers": [0, 0, 154, 14, 14, 56, 3, 3, 0],
        "machinery": [0, 0, 117, 6, 6, 40, 7, 7, 0],
        "alloys": [0, 0, 78, 1, 1, 17, 31, 31, 0],
        "firearms": [0, 0, 124, 13, 13, 29, 7, 7, 0],
        "furs": [0, 0, 176, -9, -9, 220, 63, 63, 0],
        "minerals": [0, 0, 32, -1, -1, 53, 3, 3, 0],
        "gold": [0, 0, 97, -1, -1, 66, 7, 7, 1],
        "platinum": [0, 0, 171, -2, -2, 55, 31, 31, 1],
        "gem_stones": [0, 0, 45, -1, -1, 250, 15, 15, 2],
        "alien_items": [0, 0, 53, 15, 0, 0, 7, 0, 0]
    };
    
    this.updateLocalCommodityDefinition = function(goodDefinition) {
    	var commodity = goodDefinition.key;
    	var oldDefs = this.$originalDefs[commodity];
    	//old style definition found for the good. calculate it the old way
    	if (oldDefs) {
    		var market_base_price = oldDefs[2];
    		var market_eco_adjust_price = oldDefs[3];
    		var market_eco_adjust_quantity = oldDefs[4];
    		var market_base_quantity = oldDefs[5];
    		var market_mask_price = oldDefs[6];
    		var market_mask_quantity = oldDefs[7];
    		var market_rnd = Math.floor(Math.random() * 256);
    
    		var economy = system.economy;
    
    		var price = (market_base_price + (market_rnd & market_mask_price) + (economy * market_eco_adjust_price)) & 255;
    		price *= 0.4;
    
    		var quantity = (market_base_quantity + (market_rnd & market_mask_quantity) - (economy * market_eco_adjust_quantity)) & 255;
    		if (quantity > 127) quantity = 0;
    		quantity &= 63;
    
    		goodDefinition.price = price * 10;
    		goodDefinition.quantity = quantity;
    	}
    	//no definition found. nullify the goods.
    	else {
    		goodDefinition.price = 0;
    		goodDefinition.quantity = 0;
    	}
    	return goodDefinition;
    };
    
    Scripts/upsMarket_earthquake.js
    "use strict";
    this.name      = "upsMarket_earthquake";
    this.author    = "spara";
    this.copyright = "2014 spara";
    this.license   = "CC BY-NC-SA 4.0";
    
    this._quake_systemList = {
    	"0":[18,24,26,34,39,67,72,78,97,133,134,137,140,147,182,203,205,214,227,231],
    	"1":[26,32,50,62,63,93,98,104,108,109,127,130,139,151,159,172,196,202,208,217,222,248],
    	"2":[19,27,33,37,38,57,84,88,99,108,110,115,116,125,132,136,157,159,168,217,221,224,235,239,252],
    	"3":[13,20,21,23,30,49,56,64,96,103,106,117,138,160,167,171,177,188,219],
    	"4":[26,28,30,36,43,71,72,105,119,120,126,129,149,177,178,182,185,217,218,220,229],
    	"5":[9,104,110,116,118,157,163,214,215,230,234,248,250,255],
    	"6":[4,10,19,25,35,39,43,51,58,69,114,124,139,142,181,188,196,212,213,215,218,229,244,249],
    	"7":[4,10,11,33,40,78,95,102,116,125,128,144,160,161,162,176,193,218,231,238,239]
    };
    
    this.$originalDefs = {
        "food": [0, 0, 19, -2, -2, 0, 1, 1, 0 ],
        "textiles": [0, 0, 20, -1, -1, 0, 3, 3, 0 ],
        "radioactives": [0, 0, 65, -3, -3, 0, 7, 3, 0 ],
        "slaves": [0, 0, 40, -5, 0, 0, 31, 0, 0],
        "liquor_wines": [0, 0, 83, -5, -5, 0, 15, 7, 0 ],
        "luxuries": [0, 0, 196, 8, 8, 0, 3, 3, 0 ],
        "narcotics": [0, 0, 235, 29, 29, 0, 120, 63, 0 ],
        "computers": [0, 0, 154, 14, 14, 0, 3, 1, 0 ],
        "machinery": [0, 0, 196, 6, 6, 0, 7, 0, 0 ],
        "alloys": [0, 0, 78, 1, 1, 0, 31, 15, 0 ],
        "firearms": [0, 0, 124, 13, 13, 0, 7, 3, 0 ],
        "furs": [0, 0, 176, -9, -9, 0, 63, 31, 0 ],
        "minerals": [0, 0, 32, -1, -1, 0, 3, 1, 0 ],
        "gold": [0, 0, 97, -1, -1, 0, 7, 3, 1 ],
        "platinum": [0, 0, 171, -2, -2, 0, 31, 15, 1 ],
        "gem_stones": [0, 0, 45, -1, -1, 0, 15, 7, 2 ],
        "alien_items": [0, 0, 53, 15, 0, 0, 7, 0, 0 ]
    };
    
    this.startUp = function() {
    	var msi = worldScripts.MarketScriptInterface_Main;
    	msi.$addMarketInterface("system_local", "$updateLocalCommodityDefinition", this.name);
    }
    
    this.$updateLocalCommodityDefinition = function(goodDefinition, station, systemID) {
    	// don't do anything in the non-earthquake systems
    	if (this._quake_systemList[galaxyNumber.toString()].indexOf(system.ID) == -1) {
    		return goodDefinition;
    	}
    	if (system.ID !== worldScripts.ups_docs.ups_dplanet || worldScripts.ups_docs.mark === false) {
            return goodDefinition;
        }
    	if (station) return goodDefinition; // shouldn't do anything at the station level
    	var commodity = goodDefinition.key;
    	var oldDefs = this.$originalDefs[commodity];
    	//old style definition found for the good. calculate it the old way
    	if (oldDefs) {
    		var market_base_price = oldDefs[2];
    		var market_eco_adjust_price = oldDefs[3];
    		var market_eco_adjust_quantity = oldDefs[4];
    		var market_base_quantity = oldDefs[5];
    		var market_mask_price = oldDefs[6];
    		var market_mask_quantity = oldDefs[7];
    		var market_rnd = Math.floor(Math.random() * 256);
    
    		var economy = system.economy;
    
    		var price = (market_base_price + (market_rnd & market_mask_price) + (economy * market_eco_adjust_price)) & 255;
    		price *= 0.4;
    
    		var quantity = (market_base_quantity + (market_rnd & market_mask_quantity) - (economy * market_eco_adjust_quantity)) & 255;
    		if (quantity > 127) quantity = 0;
    		quantity &= 63;
    
    		goodDefinition.price = price * 10;
    		goodDefinition.quantity = quantity;
    	}
    	//no definition found. nullify the goods.
    	else {
    		goodDefinition.price = 0;
    		goodDefinition.quantity = 0;
    	}
    	return goodDefinition;
    };
    
    Scripts/upsMarket_pirateupsbase.js
    "use strict";
    this.name      = "upsMarket_pirateupsbase";
    this.author    = "spara";
    this.copyright = "2014 spara";
    this.license   = "CC BY-NC-SA 4.0";
    
    this.$originalDefs = {
        "food": [0, 0, 19, -2, 0, 0, 1, 0, 0],
        "textiles": [0, 0, 20, -1, 0, 0, 3, 0, 0],
        "radioactives": [0, 0, 42, -3, -3, 1, 7, 3, 0],
        "slaves": [0, 0, 40, -5, 0, 0, 31, 0, 0],
        "liquor_wines": [0, 0, 105, -5, 0, 0, 15, 0, 0],
        "luxuries": [0, 0, 236, 8, 0, 0, 3, 0, 0],
        "narcotics": [0, 0, 235, 29, 0, 0, 120, 0, 0],
        "computers": [0, 0, 51, 14, 0, 0, 3, 0, 0],
        "machinery": [0, 0, 117, 6, 0, 0, 7, 0, 0],
        "alloys": [0, 0, 78, 1, 0, 0, 31, 0, 0],
        "firearms": [0, 0, 32, 13, 0, 0, 7, 0, 0],
        "furs": [0, 0, 36, -9, 0, 0, 63, 0, 0],
        "minerals": [0, 0, 16, -1, -1, 85, 3, 3, 0],
        "gold": [0, 0, 73, -1, -1, 5, 7, 3, 1],
        "platinum": [0, 0, 145, -2, -2, 6, 31, 7, 1],
        "gem_stones": [0, 0, 25, -1, -1, 250, 15, 15, 2],
        "alien_items": [0, 0, 22, 15, 0, 0, 7, 0, 0]
    };
    
    this.updateLocalCommodityDefinition = function(goodDefinition) {
    	var commodity = goodDefinition.key;
    	var oldDefs = this.$originalDefs[commodity];
    	//old style definition found for the good. calculate it the old way
    	if (oldDefs) {
    		var market_base_price = oldDefs[2];
    		var market_eco_adjust_price = oldDefs[3];
    		var market_eco_adjust_quantity = oldDefs[4];
    		var market_base_quantity = oldDefs[5];
    		var market_mask_price = oldDefs[6];
    		var market_mask_quantity = oldDefs[7];
    		var market_rnd = Math.floor(Math.random() * 256);
    
    		var economy = system.economy;
    
    		var price = (market_base_price + (market_rnd & market_mask_price) + (economy * market_eco_adjust_price)) & 255;
    		price *= 0.4;
    
    		var quantity = (market_base_quantity + (market_rnd & market_mask_quantity) - (economy * market_eco_adjust_quantity)) & 255;
    		if (quantity > 127) quantity = 0;
    		quantity &= 63;
    
    		goodDefinition.price = price * 10;
    		goodDefinition.quantity = quantity;
    	}
    	//no definition found. nullify the goods.
    	else {
    		goodDefinition.price = 0;
    		goodDefinition.quantity = 0;
    	}
    	return goodDefinition;
    };
    
    Scripts/upsMidair.js
    "use strict";
    this.name      = "upsMidair";
    this.author    = "eric walch, spara";
    this.copyright = "2008 eric walch. 2014 spara";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.collisionDistance = this.ship.collisionRadius + player.ship.collisionRadius;
        if (!worldScripts.randomshipnames)
    		this.shipName = this.ship.displayName;
        this.countdown = 5;
    
    	//name escorts with randomshipnames, if present -spara-
    	if (worldScripts.randomshipnames && this.ship.escorts) {
    		for (var i = 0; i < this.ship.escorts.length; i++) {
    			var shipName = worldScripts.randomshipnames.$randomHunterName(ship);
    			this.ship.escorts[i].displayName = this.ship.escorts[i].displayName + ": " + shipName;
    		}
    	}
    	//order escorts with escort formations, if present -spara-
    	if (worldScripts["Escort Formations Randomiser"])
    		worldScripts["Escort Formations Randomiser"].$setupDefensiveEscorts(this.ship);
    
        delete this.shipSpawned;
    }
    
    this.checkDistance = function ()
    {
      var distance = Math.round(player.ship.position.distanceTo(this.ship));
      if (distance < 6000)
      {
          player.consoleMessage("You are on final approach, lower your speed", 9);
          if (!worldScripts.randomshipnames)
    		this.ship.displayName = this.shipName + " with packages";
          this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
      }
    }
    
    this.guideIn = function ()
    {
        var distance = Math.round(player.ship.position.distanceTo(this.ship) - this.collisionDistance);
        if (distance < 0) distance = 0;
        var speed = Math.round(player.ship.speed);
        if (distance > 7000)
        {
            this.ship.reactToAIMessage("UPS_PLAYER_LEAVING");
            return;
        }
        if (distance > 1500)
        {
            if (speed > 100) player.consoleMessage("Approaching with: " + speed + "km/h, lower your speed.", 5);
        }
        else
        {
            if (speed > 50) player.consoleMessage("Approaching with: " + speed + "km/h, lower your speed to 50 km/h.", 4);
            player.consoleMessage("You are at " + distance + " meters, come to a complete standstill within 100 meters.", 4);
        }
        if (distance < 100 && speed == 0)
        {
            this.ship.reactToAIMessage("UPS_PLAYER_CLOSE");
        }
    }
    
    this.checkSpeed = function ()
    {
      if (player.ship.speed > 5)
      {
        player.consoleMessage("You are moving to much, lower your speed", 4);
        this.ship.reactToAIMessage("UPS_PLAYER_APPROACHING");
        this.countdown = 5;
      }
      else
      {
        player.consoleMessage("Transfering parcels..... " +this.countdown, 3);
        this.countdown--;
        if (this.countdown<0)
        {
            this.ship.reactToAIMessage("UPS_TRANSFERING");
        }
      }
    }
    
    this.transfer_package = function ()
    {
        player.consoleMessage("Make room or you will be sucked in my wormhole... ", 8);
        if (!worldScripts.randomshipnames)
    		this.ship.displayName = this.shipName + " without packages";
        missionVariables.ups_pplanetname = system.name;
        worldScripts["ups_parcel"].ups_pplanet = system.ID;
        missionVariables.ups_parcel_appearance = expandDescription("[ups_numbers] [ups_colors] %I");
        mission.setInstructionsKey("ups_parcel_midair", "ups_parcel");
        worldScripts["ups_parcel"].upsMarkSystem(system.ID, "ups_parcel");
        worldScripts["ups_parcel"].ups_parcel = "MIDAIR_DELIVERY";
    }
    
    Scripts/upsPirateCapsule.js
    "use strict";
    this.name			= "upsPirateCapsule";
    this.author			= "eric Walch";
    this.copyright		= "2008 eric walch";
    this.comment		= "Script for the pirate escape pods in the phyton mission"
    this.version		= "1.1";
    this.license        = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        // this.ship.setCargo("slaves", 1)
    };
    
    
    
    this.shipWasScooped = function (scooper)
    {
    	 if (scooper.isPlayer && worldScripts["ups_container"].ups_container === "FOUND2")
    	 {
    		worldScripts["ups_container"].ups_container_award += 750;
            if (++worldScripts["ups_container"].ups_container_count < 5)
            {
                this.ship.displayName = expandDescription("Pirate capsule nr " + worldScripts["ups_container"].ups_container_count);
            }
            else
            {
                this.ship.displayName = expandDescription("Last pirate capsule");
                mission.setInstructionsKey("ups_containers_small2a", "ups_container");
            }
    	 }
    };
    
    Scripts/upsPiratebase.js
    "use strict";
    this.name      = "upsPiratebase";
    this.author    = "eric walch";
    this.copyright = "2008 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.ReserveEnergy = this.ship.maxEnergy * missionVariables.ups_c2count * 0.25
        // make ship 25% stronger on every kill.
        delete this.shipSpawned;
    
    	if (this.ship.group)
        {
            this.ship.group.name = "Pirate Group";
            var defenders = system.shipsWithRole("upsdefender", this.ship, 50000)
            for (var i=0; i<defenders.length; i++)
            {
                this.ship.group.addShip(defenders[i]);
            }
        }
    }
    
    this.shipDied = function ()
    {
        var ups_container = worldScripts["ups_container"].ups_container;
        if (ups_container === "SEARCHING3" || ups_container === "JOINED_PIRATES" || ups_container === "JOINED_PIRATES2") worldScripts["ups_container"].ups_container = "FOUND3";
        if (ups_container === "SEARCHING4") worldScripts["ups_container"].ups_container = "FOUND4";
        missionVariables.TL_FOR_EQ_UPS_POLICE_SCANNER = null;
        missionVariables.TL_FOR_EQ_UPS_IDCLEANER = null;
        this.ship.commsMessage("Next time we will have an even stronger base!");
        if (worldScripts["ups_container"].ups_cloaking && worldScripts["ups_container"].ups_cloaking === "CRIPPLED")
        {
            var damaged = (player.ship.equipmentStatus("EQ_CRIPPLED_CLOAKING") === "EQUIPMENT_DAMAGED"); // even the cripled one can get damaged in fight.
    
            if (player.ship.equipmentStatus("EQ_CRIPPLED_CLOAKING") !== "EQUIPMENT_UNAVAILABLE") // player has the equipment
    		{
    			player.ship.removeEquipment("EQ_CRIPPLED_CLOAKING");
    			player.ship.awardEquipment("EQ_CLOAKING_DEVICE");
                if (damaged) player.ship.setEquipmentStatus("EQ_CLOAKING_DEVICE", "EQUIPMENT_DAMAGED");
    		}
    		if (!damaged) player.consoleMessage("Cloaking ability restored", 4);
        }
    	mission.setInstructionsKey("ups_containers_small4b", "ups_container");
        worldScripts["ups_container"].upsUnmarkSystem(missionVariables.ups_cplanet, "ups_container");
    
        delete this.shipDied;
    }
    
    this.shipBeingAttacked = function (attacker)
    {
        if (this.ship.energy < this.ship.maxEnergy * 0.5 && this.ReserveEnergy > 0)
        {
            if (this.ReserveEnergy > 500)
            {
                this.ReserveEnergy -= 500;
                this.ship.energy += 500;
                this.ship.commsMessage("You can shoot now, but we win at the end.");
            }
            else
            {
                this.ship.energy += this.ReserveEnergy;
                this.ReserveEnergy = 0;
            }
        }
    }
    
    this.stationLaunchedShip = function (ship)
    {
        if (ship.primaryRole == "defense_ship") ship.switchAI("upsPirateBasePirateAI.plist");
    	if (ship.primaryRole == "scavenger"  && !ship.bounty) ship.bounty = Math.random()*30;
    }
    
    Scripts/upsPoliceScanner.js
    "use strict";
    this.name			= "upsPoliceScanner";
    this.author			= "Eric Walch";
    this.copyright		= "2010 Eric";
    this.description	= "Script for special ups equipment.";
    this.version		= "1.0";
    this.license        = "CC BY-NC-SA 3.0";
    
    
    this.activated = function()
    {
        worldScripts["ups_container"].upsPoliceScanner();
    }
    
    Scripts/upsPrivateEscort.js
    "use strict";
    this.name           = "upsPrivateEscort";
    this.author         = "Eric Walch";
    this.copyright      = "2008 eric walch";
    this.description    = "Script for the escorts as equipment";
    this.license        = "CC BY-NC-SA 3.0";
    
    
    this.shipDied = function()
    {
        if (missionVariables.ups_private_escorts > 0) --missionVariables.ups_private_escorts
        player.consoleMessage("Trainee escort died, " + missionVariables.ups_private_escorts + " escorts left.", 6)
    }
    
    this.findPlayerHostiles = function()
    {
        function isHostileToPlayer(entity)
        {
            return (entity.isShip && entity.target && entity.target === player.ship && entity.hasHostileTarget);
        }
        if (this.ship.target == player.ship && player.ship.target && player.ship.target.bounty > 10)
        {
            this.ship.target = player.ship.target
            this.ship.reactToAIMessage("TARGET_FOUND");
            //log(this.name, "UPS escort is going to attack: "+this.ship.target.name);
            this.sendMessage("I'll help you killing that "+this.ship.target.name+".")
        }
        else
        {
            var targets = system.filteredEntities(this, isHostileToPlayer, this.ship, this.ship.scannerRange)
            if (targets.length > 0)
            {
                this.ship.target = targets[0];
                this.ship.reactToAIMessage("TARGET_FOUND");
                //log(this.name, "UPS escort is going to attack: "+targets[0].name);
                this.sendMessage("I'll take care of the "+this.ship.target.name+".")
            }
            else this.ship.reactToAIMessage("NOTHING_FOUND");
        }
    }
    
    this.signOn = function()
    {
        if(missionVariables.ups_private_escorts == 1) this.sendMessage("I'll escort you.")
        else this.sendMessage("All " + missionVariables.ups_private_escorts + " of us will escort you.")
    }
    
    this.searchMother = function()
    {
        this.ship.target = player.ship;
    }
    
    this.sendMessage = function(message)
    {
    	if (worldScripts["ups_docs"].messageAllowed()) this.ship.commsMessage(message)
        // no messages within 5 secs to avoid screen clutter.
    }
    
    
    /*
    This is currently not working because of mass locking.
    Should work with Oolite 1.75 in a different form though because of the introduced player wormhole.
    
    this.playerWillEnterWitchspace = function ()
    {
        if (missionVariables.ups_private_escorts && missionVariables.ups_private_escorts > 0)
        {
            this.ship.fuel = 7;
            this.ship.position = new Vector3D(-10E6*Math.random(), -50E8*Math.random(), -60E9);
            var success = this.ship.exitSystem(player.ship.targetSystem);
            if (!success) log(this.ship, "A private escort had a mass lock problem");
        }
    }
    */
    
    Scripts/upsPython.js
    "use strict";
    this.name      = "upsPython";
    this.author    = "eric walch, spara";
    this.copyright = "2008 eric walch. 2014 spara";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.difficulty = parseInt(this.ship.scriptInfo.difficulty);
        var missilesAvailable = ["EQ_HARDENED_MISSILE", "EQ_HARDENED_MISSILE"];
        if (worldScripts["Missiles & Bombs"])
        {
            // if M&B is installed, use some of that missiles.
            missilesAvailable.push("EQ_RMB_FRAG_MISSILE", "EQ_RMB_OVERRIDE_MISSILE");
        }
        if (this.difficulty >= 2)
        {
            if (EquipmentInfo.infoForKey("EQ_HARPOON_NUKE1_MISSILE"))
            {
                // if nukes.oxp is installed, use some of that missiles.
                missilesAvailable.push("EQ_HARPOON_NUKE1_MISSILE", "EQ_HARPOON_NUKE3_MISSILE");
            }
        }
        if (this.difficulty == 3)
        {
            if (EquipmentInfo.infoForKey("EQ_I_MISSILE"))
            {
                // if i-missile.oxp is installed, use some of that missiles.
                if (Math.random() < 0.33) missilesAvailable.push("EQ_I_MISSILE");
            }
        }
        var missileCount = missilesAvailable.length;
        var capacity = this.ship.missileCapacity;
    
        for (var i=0; i < capacity; i++)
        {
            // now fill the bay randomly with above missiles
            this.ship.awardEquipment(missilesAvailable[Math.floor(Math.random() * missileCount)]);
        }
    	//name escorts with randomshipnames, if present -spara-
    	if (worldScripts.randomshipnames && this.ship.escorts) {
    		for (var i = 0; i < this.ship.escorts.length; i++) {
    			var shipName = worldScripts.randomshipnames.$randomHunterName(ship);
    			this.ship.escorts[i].displayName = this.ship.escorts[i].displayName + ": " + shipName;
    		}
    	}
    	//order escorts with escort formations, if present -spara-
    	if (worldScripts["Escort Formations Randomiser"])
    		worldScripts["Escort Formations Randomiser"].$setupDefensiveEscorts(this.ship);
        delete this.shipSpawned;
    }
    
    
    this.shipDied = function (whom, why)
    {
       log("UPS-Courier", "Hostile Python killed by " + whom + " by means of " + why);
       if ((whom && whom.isPlayer) || (this.time && this.time + 10 > clock.absoluteSeconds))
       {
            this.ship.commsMessage("I'll be back with help for revenge!!");
            this.ship.spawn("pirate-upscapsule", 5);
            this.ship.spawn("ups-metalcontainer", 7);
            this.ship.spawn("cargopod", 8);
            worldScripts["ups_container"].ups_container_count = 0;
            worldScripts["ups_container"].ups_container_award = 0;
            worldScripts["ups_container"].ups_container = "FOUND2";
            mission.setInstructionsKey("ups_containers_small2c", "ups_container");
            worldScripts["ups_container"].upsUnmarkSystem(worldScripts["ups_container"].ups_cplanet, "ups_container");
        }
        else
        {
            // when python is killed by NPC, no variable is set and the mainscript adds a new one on next launch.
            log("UPS-Courier", "New target Python will be added on next launch or witchspace jump");
        }
        delete this.shipDied;
    }
    
    this.shipBeingAttacked = function (whom)
    {
        // check to ensure the player gets the award, even when he did not make the final kill.
        if (whom && whom.isPlayer) this.time = clock.absoluteSeconds;
    }
    
    Scripts/upsRevoltingSlavePilot.js
    "use strict";
    this.name      = "upsRevoltingSlavePilot.js";
    this.author    = "eric walch";
    this.copyright = "2010 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    // Unloading this pilot will start the homelands slave mission.
    
    this.unloadCharacter = function ()
    {
        worldScripts["ups_slaves"].ups_slavename = expandDescription("[nom11] [nom1]");
        player.addMessageToArrivalReport(expandMissionText("ups_slave_thanks",
                {ups_slavename: worldScripts["ups_slaves"].ups_slavename, dockedStationName: player.ship.dockedStation.displayName}));
        player.credits += 150;
        worldScripts["ups_slaves"].ups_slaves = "SLAVE";
    }
    
    Scripts/upsSatelite.js
    "use strict";
    this.name        = "upsSatelite";
    this.author      = "eric walch";
    this.copyright   = "2008 eric walch.";
    this.description = "Code for an upright satelite";
    this.license     = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
    	var targetVector = this.ship.position.subtract(system.mainPlanet.position).direction();
        this.ship.orientation = targetVector.rotationTo(new Vector3D(0,0,1));
    
    	delete this.shipSpawned;
    }
    
    this.shipDied = function ()
    {
        this.ship.commsMessage("Mayday, mayday, radar down!");
        var orientation = this.ship.orientation;
        if (--worldScripts["ups_slaves"].ups_satellitecount == 0)
        {
            worldScripts["ups_slaves"].ups_slaves = "DEBRIEFING";
            this.ship.spawn("ups-slave-shuttle", 5).forEach(function (ship) {ship.orientation = orientation});
        }
    }
    
    this.launchSatelliteDefenders = function ()
    {
    	var orientation = this.ship.orientation;
    	if (this.ship.target)
        {
    		if (this.ship.target.alertCondition < 3) this.ship.target.alertCondition++;
            this.ship.target.spawn("ups_satellite_defense", 3).forEach(function (ship) {ship.orientation = orientation});
        }
    }
    this.increaseTargetAlertLevel = function ()
    {
    	if (this.ship.target)
        {
    		if (this.ship.target.alertCondition < 3) this.ship.target.alertCondition++;
        }
    }
    
    /*
    This satelite has role "station" for historical reasons: This way they could communicate over alert
    levels with the legacy scripting.
    */
    
    Scripts/upsScavenger.js
    "use strict";
    this.name      = "upsScavenger";
    this.author    = "eric walch";
    this.copyright = "2008 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    this.shipDied = function ()
    {
        this.ship.spawn("ups-cargopod", Math.round(Math.random()*5) + 1);
    }
    
    Scripts/upsSlaveCobra.js
    "use strict";
    this.name           = "upsSlaveCobra";
    this.author         = "Eric Walch";
    this.copyright      = "2008 eric walch";
    this.description    = "ship far the slave missions";
    this.license        = "CC BY-NC-SA 3.0";
    
    this.shipDied = function ()
    {
        this.ship.spawn("ups-slavebarrel", 1);
    }
    
    /*
    This slave barrel starts the slave missions
    */
    
    Scripts/upsSlaveConvoy.js
    "use strict";
    this.name           = "upsSlaveConvoy";
    this.author         = "Eric Walch";
    this.copyright      = "2008 eric walch";
    this.description    = "1.72 integration";
    this.license        = "CC BY-NC-SA 3.0";
    
    this.shipDied = function (whom)
    {
        this.ship.spawn("ups-slave_convoy_barrel", 2)
        if (whom && whom.isPlayer)  worldScripts["ups_slaves"].ups_convoy = "KILLED"
    }
    
    Scripts/upsSlaveConvoyPilot.js
    "use strict";
    this.name      = "upsSlaveConvoyPilot";
    this.author    = "eric walch";
    this.copyright = "2010 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    // Unloading pilots for the slave convoy mission.
    
    this.unloadCharacter = function ()
    {
        worldScripts["ups_slaves"].ups_sl2count++;
        if (worldScripts["ups_slaves"].ups_convoy && worldScripts["ups_slaves"].ups_convoy == "ADDED")
        {
            worldScripts["ups_slaves"].ups_convoy = "KILLED";
            player.addMessageToArrivalReport(expandMissionText("ups_slave_rescue_docking"), {ups_slavename: worldScripts["ups_slaves"].ups_slavename});
        }
    }
    
    Scripts/upsSlaveRunnerPilot.js
    "use strict";
    this.name      = "upsSlaveRunnerPilot";
    this.author    = "eric walch";
    this.copyright = "2010 - 2013, eric walch.";
    
    // Unloading this pilot for the first time will start the slave missions.
    
    this.unloadCharacter = function ()
    {
        if (!worldScripts["ups_slaves"].ups_slaverescue && worldScripts["ups_parcel"].ups_pcount > 8)
        {
            // start the slave missions when not already active.
            worldScripts["ups_slaves"].ups_slaverescue = true;
        }
        var credits = (0 < oolite.compareVersion("1.77")) ?
                        Math.ceil(Math.random()*90) + 10 :
                        player.ship.dockedStation.market.slaves.price / 10;
        player.addMessageToArrivalReport(expandMissionText("ups_slave_runner",{ups_credits: credits.toFixed(1)}));
        player.credits += credits;
    }
    Scripts/upsSlaveShuttle.js
    "use strict";
    this.name           = "upsSlaveShuttle";
    this.author         = "Eric Walch";
    this.copyright      = "April 2008 eric walch";
    this.description    = "Rising shuttle for slave mission";
    this.version        = "1.00";
    
    this.shipDied = function ()
    {
        worldScripts["ups_slaves"].ups_slavesrescued--;
    };
    
    /*
    Maybe we should select a random ship with "shuttle" role and attach this script in future instead of a predefined ship?
    */
    Scripts/upsSpilledBarrel.js
    "use strict";
    this.name			= "upsSpilledBarrel";
    this.author			= "eric Walch";
    this.copyright		= "2008 eric walch";
    this.comment		= "Script for the ups-spilled-container. Used by the Boa mission and the spillage mission."
    this.license        = "CC BY-NC-SA 3.0";
    
    this.shipWasScooped = function (scooper)
    {
    	 ++worldScripts["ups_container"].ups_total_container_count;
    	 --worldScripts["ups_container"].ups_container_remaining;
    	 if (scooper.isPlayer)
    	 {
    		++worldScripts["ups_container"].ups_container_count;
    		worldScripts["ups_container"].ups_container_award += 500;
    	 }
    	 else
    	 {
    		player.consoleMessage("ups-container nr " + worldScripts["ups_container"].ups_total_container_count + " scooped by "+scooper.displayName);
    	 }
    
    	 if (worldScripts["ups_container"].ups_container === "SPILLAGE3")
         {
            if (worldScripts["ups_container"].ups_container_remaining > 0)
            {
                mission.setInstructionsKey(expandMissionText("ups_container_scattered_small1",
                    {ups_container_remaining: worldScripts["ups_container"].ups_container_remaining}), "ups_container");
            }
            else
            {
                mission.setInstructionsKey("ups_container_scattered_small3", "ups_container");
                player.consoleMessage(expandDescription("Last ups-container scooped, return to base."));
            }
         }
    	 // do something with NPC once possible
    }
    
    this.shipDied = function()
    {
    	 ++worldScripts["ups_container"].ups_total_container_count;
    	 --worldScripts["ups_container"].ups_container_remaining;
    	 player.consoleMessage("ups-container nr " + worldScripts["ups_container"].ups_total_container_count + " is destroyed");
    	 if (worldScripts["ups_container"].ups_container === "SPILLAGE3") mission.setInstructionsKey("ups_container_scattered_small1", "ups_container");
    }
    
    Scripts/upsSplinter.js
    "use strict";
    this.name			= "upsSplinter";
    this.author			= "Eric Walch";
    this.copyright		= "2008 eric walch";
    this.comment		= "Ship script for gold/platinum splinters.";
    this.license        = "CC BY-NC-SA 3.0";
    
    // initialising splinter
    this.initialising = function ()
    {
    	//log("ups", "added valuabels");
        this.amount = Math.ceil(Math.random() * 12);
        this.metal = Math.random() < 0.40 ? "platinum" : "gold";
        this.ship.setCargo(this.metal, this.amount);
        this.init = true;
    }
    
    this.shipWasScooped = function (scooper)
    {
    	//log("ups", "scooped");
    	if (!this.init) this.initialising();
        if (scooper.isPlayer)
    	{
    		player.consoleMessage(this.amount + (this.amount === 1 ? " kilogram " : " kilograms ") + this.metal);
    	}
    }
    
    Scripts/upsSunResearcher.js
    "use strict";
    this.name           = "upsSunResearcher";
    this.author         = "Eric Walch";
    this.copyright      = "2008 eric walch";
    this.description    = "ship far the solar missions";
    this.license        = "CC BY-NC-SA 3.0";
    
    this.shipDied = function (whom)
    {
        worldScripts["ups_sun"].researcher_count++;
    }
    
    Scripts/upsSunResearcherPilot.js
    "use strict";
    this.name      = "upsSunResearcherPilot";
    this.author    = "eric walch";
    this.copyright = "2010 eric walch.";
    this.license   = "CC BY-NC-SA 3.0";
    
    // Unloading pilots for the final sun mission.
    
    this.unloadCharacter = function ()
    {
        worldScripts["ups_sun"].ups_s2count++;
        worldScripts["ups_sun"].ups_s2award += 150;
        if (worldScripts["ups_sun"].ups_rescue == "YES")
        {
            player.addMessageToArrivalReport(expandMissionText("ups_researcher_thanks", {dockedStationName: player.ship.dockedStation.displayName}));
            worldScripts["ups_sun"].ups_rescue = "PILOT";
        }
    }
    
    Scripts/upsTrader.js
    "use strict";
    this.name        = "upsTrader";
    this.author      = "eric walch, spara";
    this.copyright   = "2008 eric walch. 2014 spara";
    this.description = "Code for a generic ups-trader";
    this.license     = "CC BY-NC-SA 3.0";
    
    this.shipSpawned = function ()
    {
        this.cargo = Math.round(this.ship.cargoSpaceCapacity / 10);
    	//name escorts with randomshipnames, if present -spara-
    	if (worldScripts.randomshipnames && this.ship.escorts) {
    		for (var i = 0; i < this.ship.escorts.length; i++) {
    			var shipName = worldScripts.randomshipnames.$randomHunterName(ship);
    			this.ship.escorts[i].displayName = this.ship.escorts[i].displayName + ": " + shipName;
    		}
    	}
    	//order escorts with escort formations, if present -spara-
    	if (worldScripts["Escort Formations Randomiser"])
    		worldScripts["Escort Formations Randomiser"].$setupDefensiveEscorts(this.ship);
        delete this.shipSpawned;
    }
    
    this.shipDied = function ()
    {
        var genericCargo = Math.ceil(this.cargo * 0.66);
        this.ship.spawn("cargopod", genericCargo);
        this.ship.spawn("ups-cargopod", this.cargo - genericCargo);
    }
    
    this.shipBeingAttacked = function ()
    {
        if(Math.random() < 0.05 && this.cargo > 0)
        {
            this.ship.ejectItem("cargopod");
            this.cargo--;
        }
    }
    
    this.checkStation = function ()
    {
       var stations = system.shipsWithRole("station", this.ship, 50000);
       if(stations.length > 0 && stations[0] !== this.lastStation)
       {
    		this.lastStation = stations[0];
    		if (Math.random() < 0.5 && stations[0] != system.mainStation && stations[0].hasNPCTraffic)
    		{
    			this.ship.target = stations[0];
    			this.ship.reactToAIMessage("UPS_STATION_FOUND");
    		}
        }
    }
    
    Scripts/upsUniversalScanner.js
    "use strict";
    this.name			= "upsUniversalScanner";
    this.author			= "Eric Walch";
    this.copyright		= "2010 Eric";
    this.description	= "Script for special ups equipment.";
    this.license        = "CC BY-NC-SA 3.0";
    
    
    this.activated = function()
    {
        worldScripts["ups_container"].upsUniversalScanner();
    }
    
    Scripts/upsViperEscort.js
    "use strict";
    this.name           = "upsViperEscort";
    this.author         = "Eric Walch, spara";
    this.copyright      = "2008 eric walch, 2014 spara";
    this.description    = "Escorts used for one single parcel mission";
    this.license        = "CC BY-NC-SA 3.0";
    
    //use randomshipnames oxp, if present -spara-
    this.shipSpawned = function() {
    	if (worldScripts.randomshipnames) {
    		var shipName = worldScripts.randomshipnames.$randomPoliceName(ship);
    		shipName = shipName.slice(0, shipName.length - 2) + "TR";
    		ship.displayName = ship.name + ": " + shipName;
    	}
    }
    
    this.shipDied = function ()
    {
        if (missionVariables.ups_escorts > 0) --missionVariables.ups_escorts;
        player.consoleMessage("Trainee escort died, " + missionVariables.ups_escorts + " escorts left.", 6);
    }
    
    this.signOn = function ()
    {
        if (missionVariables.ups_escorts === 1) this.sendMessage("I'll escort you.")
        else this.sendMessage("All " + missionVariables.ups_escorts + " of us will escort you.");
    }
    
    this.startAttack = function ()
    {
        if (this.ship.target.bounty === 0)
        {
            this.ship.reactToAIMessage("TARGET_LOST");
            return;
        }
    
        if (player.ship.target && player.ship.target === this.ship.target)
            this.sendMessage("I'll help you killing that " + this.ship.target.name + ".")
        else
            this.sendMessage("I'll take care of the " + this.ship.target.name + ".");
    }
    
    this.sendMessage = function (message)
    {
    	if (worldScripts["ups_docs"].messageAllowed()) this.ship.commsMessage(message);
        // no messages within 5 secs to avoid screen clutter.
    }
    
    this.searchMother = function ()
    {
        this.ship.target = player.ship;
    }
    
    Scripts/ups_conditions.js
    "use strict";
    this.name        = "UPS_Conditions";
    this.author      = "phkb";
    this.copyright   = "2015 phkb";
    this.description = "Condition script for equipment.";
    this.licence     = "CC BY-NC-SA 3.0";
    
    //-------------------------------------------------------------------------------------------------------------
    this.allowAwardEquipment = function(equipment, ship, context) {
    
    	if (context == "scripted") return true;
    	if (context == "npc") return false;
    
    	switch (equipment) {
    		case "EQ_CRIPPLED_CLOAKING": return false;
    		case "EQ_UPS_POLICE_SCANNER":
    		case "EQ_UPS_IDCLEANER":
    			if (system.info.government == 0) {
    				return true;
    			} else {
    				return false;
    			}
    		case "EQ_UPS_UNIVERSAL_SCANNER": return false;
    		case "EQ_UPS_PRIVATE_ESCORT":
    			if (system.info.government > 3 && !missionVariables.ups_private_escorts && player.ship.dockedStation.isMainStation && player.bounty == 0) {
    				return true;
    			} else {
    				return false;
    			}
    	}
    
    	return true;
    }
    
    Scripts/ups_container.js
    "use strict";
    
    this.name        = "ups_container";
    this.author      = "eric walch";
    this.copyright   = "2008 eric walch.";
    this.license     = "CC BY-NC-SA 3.0";
    this.description = "Container missions of UPS Courier";
    
    this.startUp = function ()
    {
        this.pirate = "pirate"; // since 1.73 pirates get a bounty
        this.tiger = (worldScripts.TigersTurf) ? "tigers" : this.pirate;
        this.upsMarkSystem = worldScripts["ups_docs"].upsMarkSystem;
        this.upsUnmarkSystem = worldScripts["ups_docs"].upsUnmarkSystem;
        if (worldScripts.BountySystem_Core) {
            var bsc = worldScripts.BountySystem_Core;
            bsc._offenceTypes["ups_policescanneruse"] = {
                description: "Using an illegal Police Scanner.",
    		    severity: 1
            };
            bsc._offenceTypes["ups_idcleanerremoved"] = {
                description: "Caught with an illegal ID Cleaner on board ship.",
    		    severity: 2
            };
            bsc._offenceTypes["ups_scannerremoved"] = {
                description: "Caught with illegal Police Scanner on board ship.",
    		    severity: 2
            };
            bsc._offenceTypes["ups_piratelife"] = {
                description: "Fraternising with and actively supporting pirate activity.",
    		    severity: 3
            };
        }
    }
    
    this.shipLaunchedFromStation = function ()
    {
        this.setUpShips();
        if (this.ups_container === "SPILLAGE2")
        {
            this.ups_container = "SPILLAGE3";
            this.spillCargo();
        }
        this.startScanners();
    }
    
    this.missionScreenOpportunity = function ()
    {
        if (player.ship.docked)
        {
            this.missionOffers();
        }
    }
    
    this.guiScreenChanged = function (newScreen, oldScreen)
    {
        if (this.restartMissionOffer && newScreen !== "GUI_SCREEN_LONG_RANGE_CHART")
        {
            this.restartMissionOffer = false;
            if (guiScreen !== "GUI_SCREEN_MISSION") this.missionOffers();
        }
    }
    
    this.missionOffers = function ()
    {
        if (this._savedTargetSet) player.ship.targetSystem = this._savedTarget
        this._savedTarget = null;
        this._savedTargetSet = false;
    
    	var containerStatus = this.ups_container;
        if (player.ship.dockedStation.isMainStation)
        {
            var ups_choices;
            if (containerStatus === "BOA" && worldScripts["ups_parcel"].ups_pcount > 2 && system.government > 3
                            && player.ship.bounty < 3 && this._reputation() > 2 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
            {
                ups_choices = "ups_container_accepted_yesnolook";
                if (!this.mark)
                {
                    var infoList = this._getSystemsInRange(35);
                    var targetSystem = infoList[Math.floor(Math.random()*infoList.length)];
                    this.ups_cplanet = targetSystem.systemID;
                    missionVariables.ups_cplanetname = targetSystem.name;
                    this.ups_distance = system.info.distanceToSystem(targetSystem).toFixed(1)
                                + " LY to the "+this.positionToDirection(targetSystem.coordinates);
                    this.mark = true;
                }
                var key;
    			switch (this.ups_ccount)
    			{
    				case 0: key = "ups_container_1st_offer"; break;
    				case 1: key = "ups_container_offer"; break;
    				default: key = "ups_container_offer_bis"; break;
    			}
                var message = expandMissionText(key, {ups_distance: this.ups_distance, ups_cplanet: this.ups_cplanet});
                mission.runScreen({screenID: "ups-container", title: "Boa Mission", message: message, overlay: this._overlay(),
                                    choicesKey: ups_choices, model: this.boa()}, this.choiceEvaluation);
                this.offering = "UPS_CONTAINER1";
                this.ups_container_count = 0;
                this.ups_container_award = 0;
            }
            if (containerStatus === "SEARCHING")
            {
                var daysPast = clock.days - this.ups_timerstart;
                if (daysPast > 30 && system.government > 1)
                {
                    this.ups_container = "NOT_NOW";
                    var message = expandMissionText("ups_container_boa_withdraw", {ups_days_past: daysPast});
                    mission.runScreen({screenID: "ups-container", title: "Boa Mission", message: message, overlay: this._overlay()});
                    delete this.ups_timerstart;
                    this.upsUnmarkSystem(this.ups_cplanet, this.name);
                    mission.setInstructionsKey(null);
                }
                else if (this.ups_cplanet === system.ID && (missionVariables.ups_closecontact === "DOCKED" || system.countShipsWithRole("boa_ptt") === 0))
                {
                    this.upsUnmarkSystem(this.ups_cplanet, this.name);
                    var infoList = this._getSystemsInRange(7);
                    var info = infoList[Math.floor(Math.random()*infoList.length)];
                    this.ups_cplanet = info.systemID;
                    missionVariables.ups_cplanetname = System.systemNameForID(info.systemID);
                    mission.runScreen({screenID: "ups-container", title: "Boa Mission", messageKey: "ups_container_missed", overlay: this._overlay()});
                    mission.setInstructions(expandMissionText("ups_containers_small1b", {ups_cplanet: this.ups_cplanet}));
                    this.upsMarkSystem(this.ups_cplanet, this.name);
    				missionVariables.ups_closecontact = "NO";
                }
            }
            if (containerStatus === "FOUND" && system.government > 1)
            {
                this.ups_container = "NOT_NOW";
                this.upsUnmarkSystem(this.ups_cplanet, this.name);
                mission.setInstructionsKey(null);
                var key;
                if (!this.ups_container_count)
                {
                    // no containers scooped.
                    if (missionVariables.ups_closecontact === "YES")
                    {
                        key = "ups_container_destroyed0";
                        player.credits += 100;
                    }
                    else
                    {
                        key = "ups_container_destroyed0a";
                    }
                }
                else
                {
                    if (this.ups_container_count === 5)
                    {
                        // all containers scooped.
                        this.ups_container_award += 1000;
                        key = "ups_container_destroyed2";
                    }
                    else
                    {
                        // some containers scooped.
                        key = "ups_container_destroyed1";
                    }
                    this.ups_ccount++;
                    player.credits += this.ups_container_award;
                }
                var message = expandMissionText(key, {ups_container_count: this.ups_container_count, ups_container_award: this.ups_container_award});
                mission.runScreen({screenID: "ups-container", title: "Boa Mission", message: message, overlay: this._overlay()});
                missionVariables.ups_target = "hostile Boa";
                if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
                if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
    			this.ups_timerstart = clock.days;
    			worldScripts["ups_docs"].increaseReputation(5);
                delete missionVariables.ups_closecontact;
                delete missionVariables.ups_b2planet; // no longer used variables
                delete missionVariables.ups_b2planetname; // no longer used variables
    			if (this.ups_total_container_count < 5) // there are still containers out there.
    			{
    				this.ups_container = "SPILLAGE3"; // go further as spillage mission.
    				this.ups_cplanet = system.ID;
    				this.ups_container_remaining = system.countShipsWithPrimaryRole("ups-spilled-container");
    				this.ups_container_count = 0;
    				this.ups_container_award = 0;
    			}
    			else
    			{
    				delete this.ups_total_container_count;
    			}
            }
            if (containerStatus === "LOGGING")
            {
                var daysPast = clock.days - this.ups_timerstart;
                if (system.government > 3 && player.ship.bounty < 3 && daysPast > 8
                            && this._reputation() > 3 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
                {
                    ups_choices = "ups_container_accepted2_yesnolook";
                    var messageKey = (!this.ups_c1count ? "ups_container_1st_logging" : "ups_container_logging");
                    var message = expandMissionText(messageKey, {ups_days_past: daysPast, ups_cplanet: this.ups_cplanet});
                    mission.runScreen({screenID: "ups-container", title: "Python Mission", message: message,
                                        overlay: this._overlay(), choicesKey: ups_choices,
                                        model: this.python()}, this.choiceEvaluation)
                    this.offering = "UPS_CONTAINER2";
                    this.ups_container_count = 0;
                    this.ups_container_award = 0;
                }
            }
            if (containerStatus === "FOUND2")
            {
                if (system.government > 1)
                {
                    this.ups_container = "NOT_NOW";
                    this.upsUnmarkSystem(this.ups_cplanet, this.name);
                    mission.setInstructionsKey(null);
                    var key;
                    if (!this.ups_container_count)
                    {
                        key = "ups_container_destroyed02";
                        player.credits += 100;
                    }
                    else
                    {
                        if (this.ups_container_count === 5)
                        {
                            this.ups_container_award += 1000;
                            key = "ups_container_destroyed22";
                        }
                        else
                        {
                            key = "ups_container_destroyed12";
                        }
                        this.ups_c1count += 1;
                        player.credits += this.ups_container_award;
                    }
                    var message = expandMissionText(key, {ups_container_count: this.ups_container_count, ups_container_award: this.ups_container_award});
                    mission.runScreen({screenID: "ups-container", title: "Python Mission", message: message, overlay: this._overlay()});
                    delete this.ups_container_award;
                    delete this.ups_container_count;
    				worldScripts["ups_docs"].increaseReputation(3);
                    if (this.ups_c1count > 4 && !missionVariables.TL_FOR_EQ_UPS_PRIVATE_ESCORT) {this.ups_container_message = 2};
                    missionVariables.ups_target = "pirate python";
                    if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
                    if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
    				this.ups_timerstart = clock.days;
                    delete this.ups_container_destroyed;
                }
                else
                {
                    if (!this.ups_container_destroyed)
                    {
                        var message = expandMissionText("ups_container_destroyed3", {ups_container_count: this.ups_container_count, dockedStationName: player.ship.dockedStation.displayName});
                        mission.runScreen({screenID: "ups-container", title: "Python Mission", message: message, overlay: this._overlay()});
                        this.upsUnmarkSystem(this.ups_cplanet, this.name);
                        if (!this.ups_container_count) mission.setInstructionsKey("ups_containers_small2b")
                        else mission.setInstructionsKey("ups_containers_small2a");
                        this.ups_container_destroyed = true;
                    }
                }
            }
            if (containerStatus === "LOGGING2")
            {
                var daysPast = clock.days - this.ups_timerstart;
                if (system.government > 3 && player.ship.bounty < 3 && daysPast > 8
                                && this._reputation() > 4 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
                {
                    ups_choices = "ups_container_accepted3_yesnolook";
                    var key;
                    if (player.ship.equipmentStatus("EQ_CLOAKING_DEVICE") === "EQUIPMENT_OK")
                    {
                        key = (!this.ups_c2count) ? "ups_container_1st_logging2cloaked" : "ups_container_logging2cloaked";
                    }
                    else
                    {
                        key = (!this.ups_c2count) ? "ups_container_1st_logging2" : "ups_container_logging2";
                    }
                    var message = expandMissionText(key, {ups_days_past: daysPast});
                    mission.runScreen({screenID: "ups-container", title: "Pirate Base Mission", message: message, overlay: this._overlay(),
                                        choicesKey: ups_choices}, this.choiceEvaluation)
                    this.offering = "UPS_CONTAINER3";
                    this.ups_container_award = 0;
                    this.ups_container_count = 0;
                }
            }
            if (containerStatus === "SEARCHING3" && this.ups_c2count > 2)
            {
                if (!missionVariables.ups_switch) missionVariables.ups_switch = this.ups_c2count
                else missionVariables.ups_switch += this.ups_c2count;
                if ((missionVariables.ups_switch - Math.random()*100) > 30 && system.ID !== this.ups_cplanet)
                {
                    this.ups_container = "SEARCHING4";
                    delete missionVariables.ups_switch;
                    this.ups_timerstart = clock.days;
                }
            }
            if (containerStatus === "FOUND3" && system.government > 1)
            {
                this.ups_container = "NOT_NOW";
                this.upsUnmarkSystem(this.ups_cplanet, this.name);
                mission.runScreen({screenID: "ups-container", title: "Pirate Base Mission", messageKey: "ups_container_base_destroyed", overlay: this._overlay()});
                mission.setInstructionsKey(null);
                if (!this.ups_c2count) this.ups_c2count = 0;
                this.ups_c2count++;
                player.credits += 2000;
    			worldScripts["ups_docs"].increaseReputation(5);
                delete missionVariables.ups_switch;
                missionVariables.ups_target = "pirate base";
                if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
                if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
    			this.ups_timerstart = clock.days;
            }
            if (containerStatus === "SEARCHING4" && system.government > 3)
            {
                if (clock.days - this.ups_timerstart > 15)
                {
                    mission.runScreen({screenID: "ups-container", title: "Pirate Base Mission", messageKey: "ups_container_qbomb", overlay: this._overlay()});
                    this.ups_timerstart = clock.days;
                }
            }
            if (containerStatus === "FOUND4" && system.government > 1)
            {
                this.ups_container = "NOT_NOW";
                this.upsUnmarkSystem(this.ups_cplanet, this.name);
                mission.runScreen({screenID: "ups-container", title: "Pirate Base Mission", messageKey: "ups_container_base2_destroyed", overlay: this._overlay()});
                mission.setInstructionsKey(null);
                if (!this.ups_c3count) this.ups_c3count = 0;
                this.ups_c3count++;
                player.credits += 4000;
                if (this.ups_c3count == 1) this.ups_container_message = 1;
                if (this.ups_c3count > 1 && player.score > 6400 && !missionVariables.TL_FOR_EQ_UPS_UNIVERSAL_SCANNER)
                {
                    this.ups_container_message = 3
                };
    			worldScripts["ups_docs"].increaseReputation(6);
                delete missionVariables.ups_switch;
                missionVariables.ups_target = "guarded pirate base";
                if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
                if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_target_killed]")});
    			this.ups_timerstart = clock.days;
            }
            if (containerStatus === "JOINED_PIRATES" && system.government > 3 && Math.random() < 0.2)
            {
                this.ups_container = "JOINED_PIRATES2";
                mission.runScreen({screenID: "ups-container", title: "Pirate Base Mission", messageKey: "ups_container_piratelife", overlay: this._overlay()});
                //player.ship.bounty += 1000;
                player.ship.setBounty(player.ship.bounty + 1000, "ups_piratelife");
    
                mission.setInstructionsKey(null);
                this.upsUnmarkSystem(this.ups_cplanet, this.name);
    			worldScripts["ups_docs"].increaseReputation(-25);
             }
            if (containerStatus === "SPILLAGE" && system.government > 3)
            {
                this.ups_container = "SPILLAGE2";
                mission.runScreen({screenID: "ups-container", title: "Container Retreival", messageKey: "ups_container_scattered_1", overlay: this._overlay()});
                this.ups_cplanet = system.ID;
                this.upsMarkSystem(this.ups_cplanet, this.name);
                mission.setInstructionsKey("ups_container_scattered_small");
            }
            if (containerStatus === "SPILLAGE3")
            {
                if (this.ups_container_count > 0)
                {
                    var message = expandMissionText("ups_container_scattered_2", {ups_container_count: this.ups_container_count, ups_container_award: this.ups_container_award});
                    mission.runScreen({screenID: "ups-container", title: "Container Retreival", message: message, overlay: this._overlay()});
                    this.ups_container_remaining = system.countShipsWithRole("ups-spilled-container");
                    player.credits += this.ups_container_award;
    				worldScripts["ups_docs"].increaseReputation(Math.ceil(this.ups_container_count/5));
                    if (this.ups_container_remaining > 0)
                    {
                        message = expandMissionText("ups_container_scattered_3", {ups_container_remaining: this.ups_container_remaining});
                        mission.addMessageText(message);
                        mission.setInstructionsKey("ups_container_scattered_small2");
    					this.ups_container_award = 0;
    					this.ups_container_count = 0;
    					this.ups_total_container_count = 0;
                    }
                    else
                    {
                        this.ups_container = "NOT_NOW";
                        this.upsUnmarkSystem(this.ups_cplanet, this.name);
                        mission.setInstructionsKey(null);
                        delete this.ups_cplanet;
    					delete this.ups_container_remaining;
    					delete this.ups_container_award;
    					delete this.ups_container_count;
    					delete this.ups_total_container_count;
                    }
                }
            }
            if (this.ups_container_message > 0 && guiScreen !== "GUI_SCREEN_MISSION")
            {
                var replacements = {ups_doc_count: worldScripts["ups_docs"].ups_dcount, ups_parcel_count: worldScripts["ups_parcel"].ups_pcount,
                                    ups_boa_count: this.ups_ccount, ups_python_count: this.ups_c1count, ups_base1_count: this.ups_c2count,
                                    ups_base2_count: this.ups_c3count}; // used to update variables in the missiontext below.
                var message;
                if (this.ups_container_message === 1)
                {
                    message = expandMissionText("ups_container_the_end", replacements);
                    mission.runScreen({screenID: "ups-container", title: "Container Endmessage", message: message, overlay: this._overlay()});
                    delete this.ups_container_message;
                }
                if (this.ups_container_message === 2 && system.government > 1 && Math.random() > 0.9)
                {
                    message = expandMissionText("ups_container_message2", replacements);
                    mission.runScreen({screenID: "ups-container", title: "UPS Escort Offer", message: message, overlay: this._overlay()});
                    missionVariables.TL_FOR_EQ_UPS_PRIVATE_ESCORT = 7;
                    delete this.ups_container_message;
                }
                if (this.ups_container_message === 3 && system.government > 1 && system.techLevel > 11 && Math.random() > 0.5)
                {
                    message = expandMissionText("ups_container_message3", replacements);
                    mission.runScreen({screenID: "ups-container", title: "UPS Scanner Offer", message: message, overlay: this._overlay()});
                    missionVariables.TL_FOR_EQ_UPS_UNIVERSAL_SCANNER = 12;
                    delete this.ups_container_message;
                }
            }
    		if ((containerStatus === "NO" || containerStatus ==="NOT_NOW") && system.government > 1)
    		{
    			if (player.ship.equipmentStatus("EQ_UPS_POLICE_SCANNER") === "EQUIPMENT_OK" && Math.random() < 0.2)
    			{
    				player.ship.removeEquipment("EQ_UPS_POLICE_SCANNER");
    				missionVariables.ups_galcop_punishment = 40;
    				mission.runScreen({screenID: "ups-container", title: "Scanner Removal", messageKey: "ups_container_scannerremoved", overlay: this._overlay()});
    				//player.ship.bounty += missionVariables.ups_galcop_punishment;
                    player.ship.setBounty(player.ship.bounty + missionVariables.ups_galcop_punishment, "ups_scannerremoved");
    			}
    			else if (player.ship.equipmentStatus("EQ_UPS_IDCLEANER") === "EQUIPMENT_OK" && Math.random() < 0.2)
    			{
    				player.ship.removeEquipment("EQ_UPS_IDCLEANER");
    				missionVariables.ups_galcop_punishment = 15;
    				mission.runScreen({screenID: "ups-container", title: "ID Removal", messageKey: "ups_container_idcleanerremoved", overlay: this._overlay()});
    				//player.ship.bounty += missionVariables.ups_galcop_punishment;
                    player.ship.setBounty(player.ship.bounty + missionVariables.ups_galcop_punishment, "ups_idcleanerremoved");
    			}
    		}
        }
        else if (player.ship.dockedStation.name === "Pirate Rock")
        {
            var containerOffer = this.offering;
            if (missionVariables.ups_ecount > 1 && (containerStatus === "LOGGING" || containerStatus === "LOGGING2" || containerStatus === "SEARCHING3" || containerStatus === "SEARCHING4"))
            {
                mission.runScreen({screenID: "ups-container", title: "Pirate Offer", messageKey: "ups_container_pirateoffer",
                                    choicesKey: "ups_container_pirateoffer_yesno"}, this.choiceEvaluation);
                this.offering = "UPS_CONTAINER_PIRATE";
            }
            if (containerOffer === "UPS_CONTAINER_PIRATE1")
            {
                mission.runScreen({screenID: "ups-container", title: "Pirate Offer", messageKey: "ups_container_pirateoffer2"});
                this.offering = "UPS_CONTAINER_PIRATE2";
            }
            if (containerOffer === "UPS_CONTAINER_PIRATE2")
            {
                mission.runScreen({screenID: "ups-container", title: "Pirate Offer", messageKey: "ups_container_pirateoffer2a"});
                this.offering = "UPS_CONTAINER_PIRATE3";
            }
            if (containerOffer === "UPS_CONTAINER_PIRATE3")
            {
                mission.runScreen({screenID: "ups-container", title: "Pirate Offer", messageKey: "ups_container_pirateoffer2b"});
                delete this.offering;
            }
        }
    }
    
    this.choiceEvaluation = function (choice)
    {
        var containerOffer = this.offering;
        delete this.offering; // delete here in case a choice creates a new offer.
        if (this.mark)
        {
            mission.unmarkSystem({
                       system: this.ups_cplanet,
                       name: "ups_marker"
            })
        }
        switch (containerOffer)
        {
            case "UPS_CONTAINER1":
            {
                this.mark = false
                if (choice === "YESCon")
                {
                    mission.setInstructionsKey("ups_containers_small1b", "ups_container");
                    this.upsMarkSystem(this.ups_cplanet, this.name);
                    this.ups_container = "SEARCHING";
                    this.ups_timerstart = clock.days;
                    missionVariables.ups_closecontact = "NO";
                    delete this.ups_distance;
                }
                else if (choice === "LOOKCon")
                {
                    this.upsShowDestination(this.ups_cplanet);
                }
                else if (choice === "NOCon")
                {
                    delete this.ups_distance;
                    this.ups_container = "NOT_NOW";
                }
                break;
            }
            case "UPS_CONTAINER2":
            {
                this.mark = false;
                if (choice === "YESCon")
                {
                    mission.setInstructionsKey("ups_containers_small2", "ups_container");
                    this.upsMarkSystem(this.ups_cplanet, this.name);
                    this.ups_container = "SEARCHING2";
                    }
                else if (choice === "LOOKCon")
                {
                    this.upsShowDestination(this.ups_cplanet);
                }
                else if (choice === "NOCon")
                {
                    this.ups_container = "NOT_NOW";
                }
                break;
            }
            case "UPS_CONTAINER3":
            {
                this.mark = false;
                if (choice === "YESCon")
                {
                    mission.setInstructionsKey("ups_containers_small4", "ups_container");
                    this.upsMarkSystem(this.ups_cplanet, this.name);
                    this.ups_container = "SEARCHING3";
                }
                else if (choice === "LOOKCon")
                {
                    this.upsShowDestination(this.ups_cplanet);
                }
                else if (choice === "NOCon")
                {
                    this.ups_container = "NOT_NOW";
                }
                break;
            }
            case "UPS_CONTAINER_PIRATE":
            {
                if (choice === "YESCon")
                {
                    player.credits += 5000;
                    this.offering = "UPS_CONTAINER_PIRATE1";
                    this.ups_container = "JOINED_PIRATES";
                    missionVariables.TL_FOR_EQ_UPS_POLICE_SCANNER = 3;
                    missionVariables.TL_FOR_EQ_UPS_IDCLEANER = 5;
                }
                else if (choice === "NOCon")
                {
                    player.ship.launch();
                    player.commsMessage("Yes, destroy the pirates.", 5);
                    this.disableCloakingDevice();
                }
                break;
            }
            default: log(this.name, "Encountered an illegal mission offer: "+this.offering);
        }
    }
    
    this.upsShowDestination = function (ID)
    {
        if (!this._savedTargetSet) this._savedTarget = player.ship.targetSystem;
        this._savedTargetSet = true;
        
        this.mark = true; // Keep this selected system.
        if (player.ship.equipmentStatus("EQ_ADVANCED_NAVIGATIONAL_ARRAY") == "EQUIPMENT_OK")
        {
            player.ship.targetSystem = ID;
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", backgroundSpecial: "LONG_RANGE_CHART_SHORTEST"});
        }
        else
        {
            mission.markSystem({
                       system: ID,
                       name: "ups_marker",
                       markerColor: "cyanColor",
                       markerScale: 2.0,
                       markerShape: "MARKER_DIAMOND"
            })
            var message = expandMissionText("ups_long_range_screen", {ups_system: System.systemNameForID(ID)});
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", message: message, backgroundSpecial: "LONG_RANGE_CHART"});
        }
    }
    
    this.shipWillEnterWitchspace = function ()
    {
        this.stopScanners();
    }
    
    this.shipExitedWitchspace = function ()
    {
        if (system.isInterstellarSpace)
        {
            if (player.credits > 10000 && missionVariables.ups_tcount > 2 && player.ship.fuel < 3 && Math.random() < 0.5)
            {
                if (worldScripts.transportSchedule || worldScripts.transportschedule !== undefined)  // has Transports.OXP installed
                    system.addShipsToRoute("ups-womafueltanker", 1, 0.2, 'wp')
                else
                    system.addShipsToRoute("ups-fueltanker", 1, 0.2, 'wp');
            }
            // this.startScanners();  // no scanning in interstellar space? One can still do it with the primed key.
            return;
        }
        this.setUpShips();
    	var containerStatus = this.ups_container;
        if (containerStatus === "NOT_NOW" && Math.random() < 0.11 && !missionVariables.ups_target) this.ups_container = "NO";
        if (containerStatus === "NO")
        {
            if (system.government > 3 && Math.random() < 0.05)
            {
                if (this.ups_ccount < 10 || Math.random() < 0.25)
                    this.ups_container = "BOA";
            }
            else if (this.ups_ccount > 1 && system.government < 2 && Math.random() < 0.15 && !system.sun.hasGoneNova)
            {
                if (this.ups_c1count > 10 && Math.random() > 0.33)
                {
                    if (Math.random() > 0.50) this.ups_container = "SPILLAGE";
                }
                else
                {
                    this.ups_container = "LOGGING";
                    this.ups_cplanet = system.ID;
                    missionVariables.ups_planetname = system.name;
                    this.ups_timerstart = clock.days;
                }
            }
            else if (this.ups_c1count > 1 && system.government === 0 && Math.random() < 0.2 && !system.sun.hasGoneNova)
            {
                if (this.ups_c2count < 10 || Math.random() < 0.33)
                {
                    this.ups_container = "LOGGING2";
                    this.ups_cplanet = system.ID;
                    missionVariables.ups_planetname = system.name;
                    this.ups_timerstart = clock.days;
                }
            }
            else if (player.credits > 10000 && system.techLevel < 6 && Math.random() < 0.04 && missionVariables.ups_tcount > 2 && !system.sun.isGoingNova)
            {
                player.ship.fuelLeakRate = 20;
                player.consoleMessage(expandDescription("[danger-fuel-leak]"), 4);
                if (worldScripts.transportSchedule)  // has Transports.OXP installed
                    system.addShips("ups-womafueltanker", 1); else system.addShips("ups-fueltanker", 1);
                system.addShipsToRoute("sunskim-trader", 2, Math.random(), "sw");
                system.addShipsToRoute("sunskim-trader", 2, Math.random(), "sw");
                system.addShipsToRoute("ups-sunskim", 2, Math.random(), "sw");
                system.addShipsToRoute("ups-sunskim", 2, Math.random(), "sw");
                system.addShipsToRoute("ups-sunskim", 2, Math.random(), "sw");
            }
        }
        else
        {
    		if (containerStatus.indexOf("SEARCHING") === 0) // one of the 4 searching modi.
            {
                if (player.score > 1000)
                {
                    system.addShipsToRoute(this.pirate, 4, Math.random(), "wp");
                    system.addShipsToRoute(this.tiger, 4, Math.random(), "wp");
                    system.addShipsToRoute("trader", 1, Math.random(), "wp");
                }
            }
            if (containerStatus.indexOf("JOINED_PIRATES") === 0) // one of the 2 joining modi.
            {
                system.addShipsToRoute("trader", (8 - system.economy), 0.25, "wp");
                if (system.economy < 4) system.addShipsToRoute("upstrader", 1);
                if (system.economy < 2)
                {
                    system.addShipsToRoute("upstrader", 2, Math.random(), "wp");
                    system.addShipsToRoute("upstrader", 1, Math.random(), "wp");
                }
                if (system.government > 4)
                {
                    system.addShipsToRoute("police", 3, Math.random(), "wp");
                    system.addShipsToRoute("police", 3);
                }
            }
            if (containerStatus === "SPILLAGE3")
            {
                if (!this.ups_container_count)
                {
                    this.ups_container = "NOT_NOW";
                    if (this.ups_cplanet)
                    {
                        this.upsUnmarkSystem(this.ups_cplanet, this.name);
                        mission.setInstructionsKey(null);
                        delete this.ups_cplanet;
                        delete this.ups_container_award;
                        delete this.ups_container_count;
                        delete this.ups_total_container_count;
    					delete this.ups_container_remaining;
                    }
                }
            }
            if (missionVariables.ups_target)
            {
                var pasedTime = clock.days - this.ups_timerstart;
                if (pasedTime > 15)
    			{
    				delete missionVariables.ups_target;
                    delete this.ups_timerstart;
    				this.ups_container = "NO";
    			}
                else
                {
                var happyTraders = system.shipsWithPrimaryRole("trader", player.ship);
                for (var i=0; i<happyTraders.length;i++)
                    if (happyTraders[i].isValid) //could have already been destroyed or not present
                    {
                        if (happyTraders[i].AI === "route1traderAI.plist" && pasedTime < 15 * Math.random())
    						happyTraders[i].switchAI("upsHailingTraderAI.plist");
                    }
                }
            }
        }
        if (player.ship.equipmentStatus("EQ_UPS_IDCLEANER") === "EQUIPMENT_OK")
        {
            if (!missionVariables.ups_status_backup && system.government === 0)
            {
                missionVariables.ups_status_backup = player.ship.bounty;
                player.ship.bounty = 0;
            }
            if (missionVariables.ups_status_backup && missionVariables.ups_status_backup > -1 && system.government > 0)
            {
                player.ship.bounty += missionVariables.ups_status_backup;
                delete missionVariables.ups_status_backup;
            }
        }
        if (system.government > 3 && this.ups_c1count > 2 && Math.random() < 0.04)
        {
            system.addShipsToRoute("derelict_ptt", 1, Math.random(), "wp");
        }
        this.startScanners();
    }
    
    this.playerEnteredNewGalaxy = function (newGalaxyNumber)
    {
        //reset missions on switching galaxy
        this.ups_container = "NOT_NOW";
        delete this.ups_cplanet;
    	delete missionVariables.ups_cplanetname;
        delete this.ups_container_count;
    	delete this.ups_container_award;
        delete this.ups_total_container_count;
    	delete missionVariables.ups_planet;
    	delete missionVariables.ups_planetname; // variable only used for missiontext & choices.
    	delete this.ups_timerstart;
        mission.setInstructionsKey(null);
        delete missionVariables.TL_FOR_EQ_UPS_POLICE_SCANNER;
        delete missionVariables.TL_FOR_EQ_UPS_IDCLEANER;
    }
    
    this.setUpShips = function ()
    {
        var containerStatus = this.ups_container;
    	if (this.ups_cloaking) this.enableCloakingDevice();
        if (containerStatus === "SEARCHING" && this.ups_cplanet === system.ID  && missionVariables.ups_closecontact !== "DOCKED")
        {
            if (system.countShipsWithRole("boa_ptt") === 0)
            {
                system.addShipsToRoute(this.boa(), 1, Math.random() * 0.5 + 0.2, "wp");
            }
            missionVariables.ups_closecontact = "NO";
        }
        if (containerStatus === "SEARCHING2" && this.ups_cplanet === system.ID)
        {
            if (system.countShipsWithRole("python_ptt") === 0)
            {
                system.addShipsToRoute(this.python(), 1, Math.random(), "pw");
            }
        }
        if (containerStatus === "SEARCHING3")
        {
            if (this.ups_cplanet === system.ID)
            {
                if (system.countShipsWithRole("upsbase") === 0)
                {
                    this.addPirateRock("pirate-upsbase", false);
                };
    			this.disableCloakingDevice();
            }
        }
        if (containerStatus.indexOf("JOINED_PIRATES") === 0)
        {
            if ((system.government === 0 && system.pseudoRandom100 < 5) || this.ups_cplanet === system.ID)
            {
                if (system.countShipsWithRole("upsbase") === 0)
                {
                    this.addPirateRock("pirate-upsbase", false);
                };
    			this.disableCloakingDevice();
            }
    
        }
        if (containerStatus === "SEARCHING4" && this.ups_cplanet === system.ID)
        {
            if (system.countShipsWithRole("upsbase") === 0)
            {
                this.addPirateRock("pirate-upsbase2", true);
                this.ups_timerstart = clock.days;
            };
    		this.disableCloakingDevice();
        }
        if (missionVariables.ups_private_escorts)
        {
            if ((clock.days - missionVariables.ups_private_escorts_date) < 11)
            {
                if (system.countShipsWithRole("ups_private_escort") === 0)
                {
                    system.addGroup("ups_private_escort", missionVariables.ups_private_escorts, player.ship.position, 15000);
                }
            }
            else
            {
                var myEscorts = system.shipsWithRole("ups_private_escort");
                for (var i=0; i < myEscorts.length; i++)
                {
                    if (myEscorts[i].isValid) myEscorts[i].AIState = "CANCEL_CONTRACT";
                }
                delete missionVariables.ups_private_escorts;
                delete missionVariables.ups_private_escorts_date;
                player.consoleMessage(expandDescription("[ups_escort_expired]"), 6);
                player.ship.removeEquipment("EQ_UPS_PRIVATE_ESCORT");  // Code for comatibility with old versions.
            }
        }
    }
    
    this.addPirateRock = function (rock, strong)
    {
        var baseposition = Math.random()*0.2+0.3;
        var position1 = Vector3D(0, 0, baseposition).fromCoordinateSystem("wpu");
        var position2 = Vector3D(0, 0, baseposition - 0.15).fromCoordinateSystem("wpu");
        var rock = system.addShips(rock, 1, position1)[0];
        system.addShips("asteroid", 8, rock.position, 15E3);
        system.addShips("police", 2, position2);
        system.addShips(this.pirate, 2, position2);
        if (strong)
        {
            system.addShips("upsdefender", 8, rock.position, 15E3);
            system.addShips(this.tiger, this.ups_c3count, rock.position, 15E3);
        }
        rock.orientation = [1,0,0,0]; // entrance towards planet.
    }
    
    this.enableCloakingDevice = function ()
    {
        if (this.ups_cloaking && this.ups_cloaking === "CRIPPLED")
        {
            var damaged = (player.ship.equipmentStatus("EQ_CRIPPLED_CLOAKING") === "EQUIPMENT_DAMAGED"); // even the cripled one can get damaged in fight.
    
            if (player.ship.equipmentStatus("EQ_CRIPPLED_CLOAKING") !== "EQUIPMENT_UNAVAILABLE") // player has the equipment
    		{
    			player.ship.removeEquipment("EQ_CRIPPLED_CLOAKING");
    			player.ship.awardEquipment("EQ_CLOAKING_DEVICE");
                if (damaged) player.ship.setEquipmentStatus("EQ_CLOAKING_DEVICE", "EQUIPMENT_DAMAGED");
    		}
    
            delete this.ups_cloaking;
        };
    }
    
    this.disableCloakingDevice = function ()
    {
    	if (player.ship.equipmentStatus("EQ_CLOAKING_DEVICE") === "EQUIPMENT_OK")
    	{
    		player.consoleMessage("Cloaking jammed", 3);
    		player.ship.removeEquipment("EQ_CLOAKING_DEVICE");
    		player.ship.awardEquipment("EQ_CRIPPLED_CLOAKING");
    		this.ups_cloaking = "CRIPPLED";
    	}
    }
    
    this.playerBoughtEquipment = function (equipment)
    {
    	if (equipment === "EQ_UPS_PRIVATE_ESCORT")
    	{
    			missionVariables.ups_private_escorts = 8;
    			missionVariables.ups_private_escorts_date = clock.days;
    			// player.ship.removeEquipment("EQ_UPS_PRIVATE_ESCORT"); // is now invisible equipment.
    	}
    	if (equipment === "EQ_UPS_IDCLEANER")
    	{
    		missionVariables.ups_status_backup = player.ship.bounty;
    		player.ship.bounty = 0;
    	}
    	if (equipment === "EQ_UPS_UNIVERSAL_SCANNER_REMOVER")
    	{
    		var refund = EquipmentInfo.infoForKey("EQ_UPS_UNIVERSAL_SCANNER").price/10 * 0.75;
            if (player.ship.equipmentStatus("EQ_UPS_UNIVERSAL_SCANNER") === "EQUIPMENT_DAMAGED") refund /= 10;
            player.ship.removeEquipment("EQ_UPS_UNIVERSAL_SCANNER");
    		player.ship.removeEquipment("EQ_UPS_UNIVERSAL_SCANNER_REMOVER");
    		player.credits += refund;
    	}
    }
    
    this.boa = function ()
    {
    	// all variations have the common role "boa_ptt".
    	var boa;
        var count = Math.floor(this.ups_ccount / 4);
        switch (count)
        {
            case 0: boa = "boa_ptt_easy"; break;
            case 1: boa = "boa_ptt_medium"; break;
            case 2: boa = "boa_ptt_hard"; break;
            default: boa = "boa_ptt_very_hard";
        }
    	return boa;
    }
    
    this.python = function ()
    {
    	// all variations have the common role "python_ptt".
    	var python
        var count = Math.floor(this.ups_ccount / 5);
        switch (count)
        {
            case 0: python = "python_ptt_easy"; break;
            case 1: python = "python_ptt_medium"; break;
            default: python = "python_ptt_hard";
        }
    	return python;
    }
    
    this.spillCargo = function ()
    {
        for (var i=0; i < 4; i++)
        {
            var scavenger = system.addShipsToRoute("scavenger", 1)[0];
            if (scavenger && scavenger.isValid) scavenger.switchAI("route1UpsScavengerAI.plist");
            system.addShipsToRoute("ups-spilled-container", 5);
            system.addShipsToRoute("cargopod", 5);
        }
        this.ups_container_count = 0;
        this.ups_total_container_count = 0;
        this.ups_container_remaining = 20;
     }
    
    this.ups_scanners = function ups_scnners()
    {
        if (player.ship.status !== "STATUS_IN_FLIGHT")
        {
            this.scannertimer.stop();
            return;
        }
        if (player.ship.equipmentStatus("EQ_UPS_POLICE_SCANNER") === "EQUIPMENT_OK")
        {
            this.upsPoliceScanner();
        }
        else if (player.ship.equipmentStatus("EQ_UPS_UNIVERSAL_SCANNER") === "EQUIPMENT_OK")
        {
            this.upsUniversalScanner();
        }
    }
    
    this.upsPoliceScanner = function()
    {
        // function is also called from equipment script.
        player.consoleMessage("scanned: "
            + system.countEntitiesWithScanClass("CLASS_POLICE") +" GalCop ships, "
            + system.countShipsWithPrimaryRole("hunter") + " hunters "
            + system.countShipsWithPrimaryRole("trader") + " traders and "
            + system.countShipsWithPrimaryRole("escort") + " escorts.", 6
        );
        if (system.government > 0 && Math.random() < 0.06)
        {
            //player.ship.bounty++;
            player.ship.setBounty(player.ship.bounty + 1, "ups_policescanneruse");
            system.addShipsToRoute("police", 1);
            system.addShipsToRoute("police", 1, Math.random(), "wp");
            if (system.government > 5) system.addShips("police", 1);
        }
    }
    
    this.upsUniversalScanner = function()
    {
        // function is also called from equipment script.
        player.consoleMessage("Scanned: "
            + system.countShipsWithPrimaryRole("pirate") + " pir, "
            + system.countEntitiesWithScanClass("CLASS_POLICE") + " pol, "
            + system.countShipsWithPrimaryRole("hunter") + " hunt, "
            + system.countShipsWithPrimaryRole("trader") + " trad, "
            + system.countShipsWithPrimaryRole("escort") + " esc, "
            + system.countEntitiesWithScanClass("CLASS_THARGOID") + " tharg.", 6
        );
    }
    
    this.positionToDirection = function(pos)
    {
        var pos1 = system.info.coordinates;
        var pos2 = new Vector3D(pos);
        var dx, dy, x2, y2;
    
        dx = pos2.x - pos1.x;
        dy = pos2.y - pos1.y;
        x2 = Math.abs(dx);
        y2 = Math.abs(dy);
    
        if (x2 > 2.41421 * y2)
        {  // x2 > y2 / Math.tan(Math.PI/8)
            return dx > 0? "east" : "west";
        }
        if (y2 > 1.732 * x2)
        {
            return dy < 0? "north" : "south";
        }
        if (dx > 0)
        {
            return dy < 0? "north-east" : "south-east";
        }
        else
        {
            return dy < 0? "north-west" : "south-west";
        }
    };
    
    this.startScanners = function ()
    {
        if (player.ship.equipmentStatus("EQ_UPS_UNIVERSAL_SCANNER") === "EQUIPMENT_OK" || player.ship.equipmentStatus("EQ_UPS_POLICE_SCANNER") === "EQUIPMENT_OK")
        {
            if (this.scannertimer)
            {
                if (!this.scannertimer.isRunning) this.scannertimer.nextTime = clock.absoluteSeconds + 20;
                this.scannertimer.start();
            }
            else
            {
                this.scannertimer = new Timer(this, this.ups_scanners, 20, 60);
            }
        }
    }
    
    this.stopScanners = function ()
    {
        if (this.scannertimer) this.scannertimer.stop();
    }
    
    this._reputation = function ()
    {
        return player.parcelReputation;
    }
    
    this._overlay = function() {
        if (player.ship.hudHidden || player.ship.hudAllowsBigGui) {
            return {name:"upsnews-large.png", height:546};
        } else {
            return {name:"upsnews-small.png", height:546};
        }
    }
    
    this._getSystemsInRange = function(range) {
        var infoList = SystemInfo.systemsInRange(range);
        // remove any systems you can't reach
        for (var i = infoList.length - 1; i >= 0; i--) {
            var myRoute = System.infoForSystem(galaxyNumber, system.ID).routeToSystem(infoList[i], player.ship.routeMode);
            if (!myRoute || myRoute.route.length === 0) {
                infoList.splice(i, 1);
            }
            myRoute = null;
        }
        return infoList;
    }
    Scripts/ups_docs.js
    "use strict";
    
    this.name        = "ups_docs";
    this.author      = "eric walch";
    this.copyright   = "2008 eric walch.";
    this.license     = "CC BY-NC-SA 3.0";
    this.description = "Docs missions of UPS Courier";
    
    //1.9 asteroids are chosen from any asteroid available instead of using special scripted asteroids.
    
    this.startUp = function ()
    {
        this.pirate = "pirate";
        if (!missionVariables.ups_docs) this.$initialise();
        this.$loadVariables();
        this.mark = false;
    	if (missionVariables.ups_galaxy != galaxyNumber)
    	{
    		log(this.name, "UPS detected the player is in an other galaxy than the last save with UPS installed. Now resetting missions.")
    		var galaxy = missionVariables.ups_galaxy;
    		if (++galaxy === 8) galaxy = 0;  // simulate jump in next galaxy since last save for proper resetting old missions.
            worldScripts["ups_docs"].playerEnteredNewGalaxy(galaxy);
            worldScripts["ups_parcel"].playerEnteredNewGalaxy(galaxy);
            worldScripts["ups_container"].playerEnteredNewGalaxy(galaxy);
            worldScripts["ups_sun"].playerEnteredNewGalaxy(galaxy);
            worldScripts["ups_slaves"].playerEnteredNewGalaxy(galaxy);
            missionVariables.ups_galaxy = galaxyNumber;
    	}
        if (!missionVariables.upsVersionUpdated)
        {
            // Uses bits to remember changes so this varriable can be used also for furure changes
            // bit 1: Markers are updated.
            this.updateUpsMarkers();
            missionVariables.upsVersionUpdated = "1";
        }
        this.cleanUpMarkers();
    }
    
    this.$initialise = function ()
    {   // setting up all mission variables.
    	missionVariables.ups_docs = "NO";
    	missionVariables.ups_parcel = "NO";
    	missionVariables.ups_container = "NO";
        missionVariables.ups_slaves = "NO";
        var initialitations = ["dcount", "pcount", "ccount", "c1count", "c2count", "ecount", "galcop_punishment", "reputation"];
        for (var i=0; i<initialitations.length; i++) missionVariables["ups_"+initialitations[i]] = 0;
    	missionVariables.ups_galaxy = galaxyNumber;  // needed to detect jumps with ups de-installed.
        // galaxyNumber is GLOBAL and may change to player.galaxyNumber  in future!!!
    }
    
    this.vars = {
        // table used for translation between missionVariables and worldScript variables. MissionVarriables are slow, so
        // the most frequent used ones are maintained as real varriables during game and only turned back into
        // missionVariables on saving.
        "ups_docs": ["ups_docs", "ups_dcount", "ups_dplanet", "ups_reputation"],
        "ups_parcel": ["ups_parcel", "ups_pcount", "ups_pplanet", "ups_ptimerstart"],
        "ups_container": ["ups_container", "ups_ccount", "ups_c1count", "ups_c2count", "ups_c3count", "ups_cplanet",
                          "ups_timerstart", "ups_container_count", "ups_container_award", "ups_container_message",
                          "ups_container_destroyed"],
        "ups_slaves": ["ups_slaves", "ups_slaverescue", "ups_slplanet", "ups_sl2planet", "ups_slcount", "ups_slavesrescued", "ups_sl2count", "ups_slavename"],
        "ups_sun": ["ups_sun", "ups_blackbox", "ups_sunvisits", "ups_sunbase", "ups_splanet", "ups_splanetname", "ups_stimerstart",
                    "ups_rescue", "ups_s2count", "ups_s2award", "ups_stationparts"]
    }
    
    this.$loadVariables = function ()
    {
        // Make sure the next variables always exist.
        var initialitations = ["dcount", "pcount", "ccount", "c1count", "c2count", "ecount", "galcop_punishment", "reputation", "galaxy"];
        for (var i=0; i<initialitations.length; i++)
        {
            if (!missionVariables["ups_"+initialitations[i]]) missionVariables["ups_"+initialitations[i]] = 0;
        }
    
    	// missionvariables are very slow. Convert the most intensive used ones to local JS variables.
        // do it in the script with the initialisation code because we don' know if the other scripts will initialise after this one.
        var missions = Object.getOwnPropertyNames(this.vars);
        var i, j;
        for (j=0; j < missions.length; j++)
        {
            var m = missions[j];
            for (i=0; i < this.vars[m].length; i++)
            {
                worldScripts[m][this.vars[m][i]] = missionVariables[this.vars[m][i]];
            }
        }
        if (missionVariables.ups_difference) delete missionVariables.ups_difference; // no longer used.
    }
    
    this.playerWillSaveGame = function ()
    {
        // update missionvariables to be stored.
        // I do it this way to stay compatible with old versions. For a new OXP it is
        // better to use the JSON function to convert all into a single mission variable.
        var missions = Object.getOwnPropertyNames(this.vars);
        var i, j;
        for (j=0; j < missions.length; j++)
        {
            var m = missions[j];
            for (i=0; i < this.vars[m].length; i++)
            {
                if (worldScripts[m][this.vars[m][i]])
                {
                    // only create mission variables for existing variables
                    missionVariables[this.vars[m][i]] = worldScripts[m][this.vars[m][i]];
                }
                else
                {
                    // make sure we don't leave unused missionVariables from a loaded game.
                    delete missionVariables[this.vars[m][i]];
                }
            }
        }
        this.cleanUpMarkers();
    }
    
    this.shipWillDockWithStation = function (station)
    {
        if (station.isMainStation)
        {
            this.ups_reports(station);
        }
    }
    
    this.shipLaunchedFromStation = function ()
    {
        this.setUpShips();
        if (this.ups_docs === "ASTEROID") this.setUpAsteroidMission();
    }
    
    this.missionScreenOpportunity = function ()
    {
        if (player.ship.docked)
        {
            this.missionOffers();
        }
    }
    
    this.requestLongRangeChart = function (system, script)
    {
        this.lookLongRangeChartScreen = true;
        this.askingScript = script;
        this.planetShowNumber = system;
    }
    
    this.longRangeChart = function longRangeChart() // routine used by 4 ups world scripts
    {
        if (guiScreen === "GUI_SCREEN_LONG_RANGE_CHART")
        {
            if (this.mark) {this.upsUnmarkSystem(this.planetShowNumber, this.name); this.mark = false}
            else {this.upsMarkSystem(this.planetShowNumber, "ups_docs"); this.mark = true};
        }
        else
        {
            this.missionChartTimer.stop();
            this.upsUnmarkSystem(this.planetShowNumber, this.name);
            this.mark = true; // Keep this selected system.
        }
    }
    
    this.guiScreenChanged = function (newScreen, oldScreen)  // routine used by all 5 ups world scripts
    {
        if (this.restartMissionOffer && newScreen !== "GUI_SCREEN_LONG_RANGE_CHART")
        {
            this.restartMissionOffer = false;
            if (guiScreen !== "GUI_SCREEN_MISSION") this.missionOffers();
        }
    	if (!this.lookLongRangeChartScreen) return;
    	if (!player.ship.docked) this.lookLongRangeChartScreen = null;
    	if (guiScreen === "GUI_SCREEN_LONG_RANGE_CHART")
    	{
    		this.lookLongRangeChartScreen = null;
    		if (this.missionChartTimer) this.missionChartTimer.start()
    		else this.missionChartTimer = new Timer(this, this.longRangeChart, 0.5, 0.5);
            worldScripts[this.askingScript].restartMissionOffer = true;
    	}
    	else player.consoleMessage("Go to the long range screen: F6/F6", 10);
    }
    
    this.ups_reports = function (station)
    {
        if (system.government === 6 && this.ups_docs === "SMALL_DELIVERY")
        {
            if (this.ups_dplanet !==  system.ID  && this.ups_dcount > 0) // The case (this.ups_dcount == 0) is handled in a missionscreen.
            {
                this.ups_docs = "NOT_NOW";
                player.credits += 100;
                player.addMessageToArrivalReport(expandMissionText("ups_docs_arrival_report"));
                mission.setInstructionsKey(null);
                this.ups_dcount++;
                missionVariables.ups_docs_appearance = null;
                this.increaseReputation(1);
            }
        }
        if (this.ups_docs === "EARTHQUAKE_DELIVERY" && this.ups_dplanet ===  system.ID)
        {
            if (missionVariables.ups_earthquake_cargo > 0 && manifest["machinery"] > 0)
            {
                var machinery = manifest["machinery"];
                if (machinery > missionVariables.ups_earthquake_cargo) machinery = missionVariables.ups_earthquake_cargo;
                player.addMessageToArrivalReport(expandMissionText("ups_earthquake_arrival_report",{machine_count: machinery}));
                manifest["machinery"] -= machinery;
                missionVariables.ups_earthquake_cargo -= machinery;
                this.machineDelivery = true;
            }
        }
    };
    
    this.addUpsContract = function ()
    {
        var thisSystem = system.info;
    
        var targetSystems = SystemInfo.filteredSystems(this, function(other){
               return (other.systemID !== thisSystem.systemID) && other.government == 6 && !system.sun.hasGoneNova;
        });
        var count = Math.ceil(Math.random()*5);
    
        for (var i = 0; i<count; i++)
        {
            var contractSystem = targetSystems[Math.floor(Math.random() * targetSystems.length)];
            var contractRoute = system.info.routeToSystem(contractSystem);
    		// if there is no route to the destination, skip this one
    		if (contractRoute == null) continue;
            var contractDescription = expandDescription("[ups_condition] [ups_document_description]");
            contractDescription = contractDescription.charAt(0).toUpperCase() + contractDescription.slice(1);
    
            var parcel =
            {
                destination: contractSystem.systemID,
                sender: "UPS courier",
                deadline: clock.seconds + contractRoute.time * 3600 + 3600*24*2,
                payment: Math.floor(contractRoute.distance * (3+Math.random()) + (Math.pow(contractRoute.route.length,1+(0.3*player.parcelReputation))) + 50),
                description: contractDescription,
                route: contractRoute
            }
    
            worldScripts["oolite-contracts-parcels"]._addParcelToSystem(parcel);
        }
    };
    
    this.missionOffers = function ()
    {
        if (this._savedTargetSet) player.ship.targetSystem = this._savedTarget
        this._savedTarget = null;
        this._savedTargetSet = false;
    
    	var docsStatus = this.ups_docs;
        if (player.ship.dockedStation.isMainStation)
        {
    
            if (docsStatus === "YES"  && system.government === 6 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
            {
                var chronicleSystem = new Array(124, 39, 36, 41, 198, 73, 149, 226)[galaxyNumber]; // define one per galaxy
    			if (player.ship.bounty < 2 && this.ups_dcount > 20 &&
                        system.ID != chronicleSystem && (Math.random() < 0.2 || this.mark))
                {
                    this.ups_dplanet = chronicleSystem;
    				missionVariables.ups_dplanetname = System.systemNameForID(chronicleSystem);
    				missionVariables.ups_docs_appearance = expandDescription("[ups_numbers] [ups_condition] %I");
    				if (!missionVariables.ups_chronicle_content)
                    {
    					missionVariables.ups_chronicle_content = expandDescription("the [25] [27] on %H and the recent [8]");
                    }
                    var message = expandMissionText("ups_docs_chronicle_offer", {ups_inhabitants: system.inhabitantsDescription, dockedStationName: player.ship.dockedStation.displayName});
                    mission.runScreen({screenID: "ups-docs", title: "Document Transport", message: message,
                                        overlay: this._overlay(), choicesKey: "ups_docs_accepted_yesnolook"},
                                        this.choiceEvaluation);
                    this.offering = "UPS_DOCS_CHRONICLE";
                }
                else if (player.ship.bounty < 3)
                {
                    missionVariables.ups_docs_appearance = expandDescription("[ups_numbers] [ups_condition] %I");
                    this.today = new Date();
                    if (this.today.getMonth()==11 && this.today.getDate() > 15 && this.today.getDate() < 26)
                        missionVariables.ups_docs_appearance += " Christmas";
    
                    var key = (!this.ups_dcount) ? "ups_docs_1st_offer" : "ups_docs_offer";
                    var message = expandMissionText(key, {dockedStationName: player.ship.dockedStation.displayName});
                    mission.runScreen({screenID: "ups-docs", title: "Document Transport", message: message,
                                            overlay: this._overlay(), choicesKey: "ups_docs_accepted_yesno"},
                                            this.choiceEvaluation)
                    this.offering = "UPS_DOCS";
                }
            }
            if (docsStatus === "SMALL_DELIVERY" && system.government === 6)
            {
                if (this.ups_dplanet ===  system.ID)
                {
                    this.ups_docs = "ACCEPTED";
                    mission.runScreen({screenID: "ups-docs", title: "Document Transport", messageKey: "ups_back_at_home_docs", overlay: this._overlay()});
                    this.increaseReputation(-1);
                }
                else
                {
                    if (!this.ups_dcount)
                    {
                        this.ups_docs = "NOT_NOW";
                        player.credits += 100;
                        var message = expandMissionText("ups_docs_1st_unloading", {populationCount: system.population * 100});
                        mission.runScreen({screenID: "ups-docs", title: "Document Transport", message: message, overlay: this._overlay()})
                        mission.setInstructionsKey(null);
                        this.ups_dcount++;
                        missionVariables.ups_docs_appearance = null;
                        this.increaseReputation(1);
                    }
                    else
                    {
                        // written in arrival report now.
                    };
                    if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_docs]")});
                    if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_docs]")});
                }
            }
            if (docsStatus === "LOGGING" && system.government === 6)
            {
                if (this._reputation() > 3)
                {
                    if (this.ups_dplanet != system.ID && !worldScripts.ups_docs.lookLongRangeChartScreen)
                    {
                        missionVariables.ups_docs_appearance = expandDescription("[ups_numbers] secret %I");
                        var message = expandMissionText("ups_docs_offer2", {dockedStationName: player.ship.dockedStation.displayName});
                        mission.runScreen({screenID: "ups-docs", title: "Document Transport", message: message,
                                            overlay: this._overlay(), choicesKey: "ups_docs_accepted_yesnolook"},
                                            this.choiceEvaluation);
                        this.offering = "UPS_DOCS2";
                    }
                    else if (this._reputation() < 0)
                    {
                       this.ups_docs = "NOT_NOW";
                    }
                }
            }
            if (docsStatus === "SPECIAL_DELIVERY")
            {
                if (this.ups_dplanet === system.ID && !missionVariables.ups_docbriefing)
                {
                    missionVariables.ups_docbriefing = "YES";
                    mission.runScreen({screenID: "ups-docs", title: "Document Transport", messageKey: "ups_docs_unloading2a", overlay: this._overlay()});
                    mission.setInstructionsKey("ups_docs_small3");
                }
            }
            if (docsStatus === "CHRONICLE_DELIVERY")
            {
                if (this.ups_dplanet === system.ID)
    			{
                    this.ups_docs = "NOT_NOW";
    				if (clock.hours/24 <= missionVariables.ups_dtimer)
    				{
                        var message = expandMissionText("ups_docs_chronicle_unloading", {dockedStationName: player.ship.dockedStation.displayName});
                        mission.runScreen({screenID: "ups-docs", title: "Document Transport", message: message, overlay: this._overlay()});
                        this.ups_dcount++;
                        player.credits += 3000;
                        this.increaseReputation(4);
                        if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_docs]")});
                        if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_docs]")});
    				}
    				else
    				{
                        mission.runScreen({screenID: "ups-docs", title: "Document Transport", messageKey: "ups_docs_chronicle_unloading_late", overlay: this._overlay()});
                        this.increaseReputation(-3);
    				}
    				this.upsUnmarkSystem(this.ups_dplanet, this.name);
    				missionVariables.ups_docs_appearance = null;
                    this.ups_dplanet = null
    				mission.setInstructionsKey(null);
    				missionVariables.ups_dtimer = null;
    				missionVariables.ups_docs_type = null;
    				missionVariables.ups_chronicle_content = null;
    			}
    			else
    			{
                    this.updateChronicleMessages();
    				if (clock.hours/24 > missionVariables.ups_dtimer + 5)
    				{
    					this.upsUnmarkSystem(this.ups_dplanet, this.name);
    					this.ups_docs = "NOT_NOW";
    					mission.runScreen({screenID: "ups-docs", title: "Document Transport", messageKey: "ups_docs_chronicle_expired", overlay: this._overlay()});
    					missionVariables.ups_docs_appearance = null;
                        this.ups_dplanet = null;
    					missionVariables.ups_dtimer = null;
    					missionVariables.ups_chronicle_content = null;
    					mission.setInstructionsKey(null);
    					this.increaseReputation(-5);
    				}
    			}
            }
            if (docsStatus === "ASTEROID_OFFER")
            {
                if (system.countShipsWithRole("asteroid") < 25) 
                {
                    mission.runScreen({screenID: "ups-docs", title: "Asteroid Mission", messageKey: "ups_docs_asteroid1",
                                        overlay: this._overlay(), choicesKey: "ups_docs_asteroid_yesno"},
                                        this.choiceEvaluation);
                    this.offering = "UPS_DOCS_ASTEROID";
                }
            }
            if (player.trumbleCount > 0 && (docsStatus === "NOT_NOW" || docsStatus === "NO"))
            {
                if (Math.random() < player.trumbleCount/20 && player.credits > 2000)
                {
                    var message = expandMissionText("ups_trumble_repellant", {ups_trumble_count: player.trumbleCount, ups_trumble_plural: (player.trumbleCount === 1 ? "" : "s")});
                    mission.runScreen({screenID: "ups-docs", title: "Document Transport", message: message,
                                        overlay: "trumblebox.png", choicesKey: "ups_trumble_repellant_yesno"},
                                        this.choiceEvaluation);
                    this.offering = "UPS_DOCS_REPELLANT";
                }
            }
            if (docsStatus === "TRUMBLE_REPELLANT" && clock.days > missionVariables.ups_dtimer + 8)
            {
                this.ups_docs = "NOT_NOW";
                if (Math.random() < 0.5) mission.runScreen({screenID: "ups-docs", title: "Document Transport", messageKey: "ups_trumble_repellant_warning",
                    overlay: this._overlay()});
                missionVariables.ups_dtimer = null;
            }
            if (docsStatus === "EARTHQUAKE_OFFER" && system.government === 6 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
            {
                if (!this.mark)
                {
                    var infoList = worldScripts.ups_container._getSystemsInRange(25);
                    var targetSystem = infoList[Math.floor(Math.random()*infoList.length)];
                    this.ups_dplanet = targetSystem.systemID;
                    missionVariables.ups_dplanetname = targetSystem.name;
                    missionVariables.ups_dplanet_inhabitants = targetSystem.inhabitants;
                    targetSystem.description = expandDescription("The planet "+ targetSystem.name
                            + " is frequently plagued by earthquakes. The last big one however, largely destroyed the planetary production capacity.");
                    targetSystem.productivity = targetSystem.productivity / 100;
                    // now contralled through planetinfo.plist and upsMarket_earthquake.js
                    //targetSystem.market = "ups_earthquake";
                    this.mark = true;
                }
                missionVariables.ups_earthquake_cargo = player.ship.cargoSpaceCapacity * 3;
                missionVariables.ups_earthquake_reward = missionVariables.ups_earthquake_cargo * 70;
                mission.runScreen({screenID: "ups-docs", title: "Machinery Transport", messageKey: "ups_earthquake_offer",
                                    choicesKey: "ups_earthquake_yesnolook", overlay: this._overlay()},
                                    this.choiceEvaluation);
                this.offering = "UPS_DOCS_EARTHQUAKE_OFFER";
            }
            if (this.ups_docs === "EARTHQUAKE_DELIVERY" && this.machineDelivery)
            {
                if (missionVariables.ups_earthquake_cargo > 0)
                {
                    mission.runScreen({screenID: "ups-docs", title: "Machinery Transport", messageKey: "ups_earthquake_delivery",
                                        overlay: this._overlay()});
                    mission.setInstructionsKey("ups_docs_earthquake");
                    this.launchEarthquakeRescue(3);
                }
                else
                {
                    this.ups_docs = "NOT_NOW";
                    mission.runScreen({screenID: "ups-docs", title: "Machinery Transport", messageKey: "ups_earthquake_final",
                                        overlay: this._overlay()});
                    mission.setInstructionsKey("ups_docs_earthquake");
                    this.upsUnmarkSystem(this.ups_dplanet, this.name);
                    // an undefined marget throws an exception in 1.74, so explicit check for existence
                    if (system.info.hasOwnProperty("market")) system.info.market = null;
                    system.info.description = null;
                    system.info.productivity = null;
                    player.credits += missionVariables.ups_earthquake_reward;
                    missionVariables.ups_earthquake_reward = null;
                    missionVariables.ups_earthquake_cargo = null;
                    mission.setInstructionsKey(null);
                    this.increaseReputation(5);
                }
                this.machineDelivery = false;
            }
        }
    	else if (player.ship.dockedStation.name.indexOf("Sun Research Station") === 0) // docked on one of the 4 research stations
        {
            if (docsStatus === "SPECIAL_DELIVERY")
            {
                this.ups_docs = "NOT_NOW";
                player.credits += 250;
                mission.runScreen({screenID: "ups-docs", title: "Document Transport", messageKey: "ups_docs_unloading2", overlay: this._overlay()});
                mission.setInstructionsKey(null);
                this.ups_dcount++;
                this.upsUnmarkSystem(this.ups_dplanet, this.name);
                this.increaseReputation(3);
                if (player.ship.dockedStation.name === "Sun Research Station Alpha" && !worldScripts["ups_sun"].ups_sun)
                {
                    worldScripts["ups_sun"].initSunMission(missionVariables.ups_dplanetname, this.ups_dplanet);
                }
                missionVariables.ups_dplanetname = null;
                missionVariables.ups_docbriefing = null;
    			missionVariables.ups_docs_type = null;
                missionVariables.ups_docs_appearance = null;
                if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_docs]")});
                if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_docs]")});
            }
        }
    }
    
    this.choiceEvaluation = function (choice)
    {
        var docOffer = this.offering;
        delete this.offering;
        switch (docOffer)
        {
            case "UPS_DOCS":
            {
                if (choice === "YESDoc")
                {
                    mission.setInstructionsKey("ups_docs_small");
                    this.ups_docs = "ACCEPTED";
                    this.ups_dplanet = system.ID;
                }
                else
                {
                    if (choice === "NODoc")
                    {
                        this.ups_docs = "NOT_NOW";
                        missionVariables.ups_docs_appearance = null;
                        this.increaseReputation(-2);
                    }
                }
                break;
            }
            case "UPS_DOCS2":
            {
                this.mark = false;
                if (choice === "YESDoc")
                {
                    mission.setInstructionsKey("ups_docs_small2");
                    this.ups_docs = "SPECIAL_DELIVERY";
                    this.upsMarkSystem(this.ups_dplanet, this.name);
                    missionVariables.ups_docs_type = "secret documents";
                }
                else if (choice === "LOOKDoc")
                {
                    this.upsShowDestination(this.ups_dplanet);
                }
                else if (choice === "NODoc")
                {
                    this.ups_docs = "NOT_NOW";
                }
                break;
            }
            case "UPS_DOCS_CHRONICLE":
            {
                this.mark = false;
                if (choice === "YESDoc")
                {
                    this.ups_docs = "CHRONICLE_DELIVERY";
                    this.upsMarkSystem(this.ups_dplanet, this.name);
                    missionVariables.ups_dtimer = clock.hours/24 + 15;
                    missionVariables.ups_docs_type = "chronicle articles";
                }
                else if (choice === "LOOKDoc")
                {
                    this.upsShowDestination(this.ups_dplanet);
                }
                else if (choice === "NODoc")
                {
                    this.ups_docs = "NOT_NOW";
                }
                break;
            }
            case "UPS_DOCS_ASTEROID":
            {
                if (choice === "YESDoc")
                {
                    this.offering === "UPS_DOCS_ASTEROID2";
                    mission.runScreen({screenID: "ups-docs", title: "Asteroid Mission", messageKey: "ups_docs_asteroid2", model: "ups-splinter"});
                    mission.setInstructionsKey("ups_docs_asteroid_short");
                    this.ups_docs = "ASTEROID";
                    missionVariables.ups_asteroid_count = 16;
                }
                else if (choice === "NODoc")
                {
                    this.ups_docs = "NOT_NOW";
                }
                break;
            }
            case "UPS_DOCS_REPELLANT":
            {
                if (choice === "YESDoc")
                {
                    player.credits -= 2000;
                    this.ups_docs = "TRUMBLE_REPELLANT";
                    missionVariables.ups_dtimer = clock.days;
                }
                else if (choice === "NODoc")
                {
                    if (Math.random() < 0.3) this.trumbleRepellant = true;
                }
                break;
            }
            case "UPS_DOCS_EARTHQUAKE_OFFER":
            {
                this.mark = false;
                if (choice === "YESDoc")
                {
                    this.ups_docs = "EARTHQUAKE_DELIVERY";
                    this.upsMarkSystem(this.ups_dplanet, this.name);
                    mission.setInstructionsKey("ups_docs_earthquake");
                    missionVariables.ups_dplanet_inhabitants = missionVariables.ups_dplanet_inhabitants.toLowerCase();
                }
                else if (choice === "LOOKDoc")
                {
                    this.upsShowDestination(this.ups_dplanet);
                }
                else if (choice === "NODoc")
                {
                    this.ups_docs = "NOT_NOW";
                }
                break;
            }
            default: log(this.name, "Encountered an illegal mission offer: "+this.offering);
        }
    }
    
    this.upsShowDestination = function (ID)
    {
        if (!this._savedTargetSet) this._savedTarget = player.ship.targetSystem;
        this._savedTargetSet = false;
        
        this.mark = true; // Keep this selected system.
        if (player.ship.equipmentStatus("EQ_ADVANCED_NAVIGATIONAL_ARRAY") == "EQUIPMENT_OK")
        {
            player.ship.targetSystem = ID;
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", backgroundSpecial: "LONG_RANGE_CHART_SHORTEST"});
        }
        else
        {
            mission.markSystem({
                       system: ID,
                       name: "ups_marker",
                       markerColor: "magentaColor",
                       markerScale: 2.0,
                       markerShape: "MARKER_DIAMOND"
            });
            var message = expandMissionText("ups_long_range_screen", {ups_system: System.systemNameForID(ID)});
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", message: message, backgroundSpecial: "LONG_RANGE_CHART"});
        }
    }
    
    this.shipExitedWitchspace = function ()
    {
        if (system.isInterstellarSpace) return;
        this.setUpShips();
    	var docStatus = this.ups_docs;
        if (docStatus === "NOT_NOW" && Math.random() < 0.11) this.ups_docs = "NO";
        if (docStatus === "ACCEPTED")
        {
            this.ups_docs = "SMALL_DELIVERY";
            system.addShipsToRoute("trader", 2, Math.random(), "pw");
        }
        if (docStatus === "NO")
        {
            if (Math.random() < 0.2 && system.government === 6)
            {
                if (this.ups_dcount > 30 && Math.random() < 0.2)
                    this.ups_docs = "EARTHQUAKE_OFFER"
                else
                    this.ups_docs = "YES";
            }
            else if (Math.random() < 0.05 && system.government === 6 && this.ups_dcount > 5 && !system.sun.hasGoneNova)
            {
                this.ups_docs = "LOGGING";
                this.ups_dplanet = system.ID;
                missionVariables.ups_dplanetname = system.name;
            }
            else if (Math.random() < 0.035 && this.ups_dcount > 20 && system.countShipsWithRole("asteroid") < 15)
            {
                this.ups_docs = "ASTEROID_OFFER";
            }
        }
        else
        {
            if (docStatus === "ASTEROID")
            {
                this.ups_docs = "NOT_NOW";
                missionVariables.ups_asteroid_count = null;
                mission.setInstructionsKey(null);
            }
            if (docStatus === "SMALL_DELIVERY")
            {
    			system.addShipsToRoute("trader", 1);
    			system.addShipsToRoute("trader", 2, Math.random(), "pw");
    		};
    		if (docStatus === "CHRONICLE_DELIVERY")
    		{
    			if (system.government < 3) this.addDocPirates(10 - 4 * system.government)
    			else if (Math.random() < 0.1) this.addDocPirates(2);
                this.updateChronicleMessages();
    		}
            if (docStatus === "SPECIAL_DELIVERY" && system.government < 2)
            {
    			this.addDocPirates(10 - 5 * system.government);
            }
        }
        if (system.government === 6 && !system.sun.hasGoneNova && system.mainStation)
        {
            this.addUpsContract();
        }
    }
    
    this.updateChronicleMessages = function ()
    {
        if (this.messagesTimer)
        {
            this.messagesTimer.start();
        }
        else
        {
            this.messagesTimer = new Timer(this, this.updateChronicleMessagesDelayed, 0, 2); // clock might not be valid yet.
        }
    }
    
    this.updateChronicleMessagesDelayed = function updateChronicleMessagesDelayed()
    {
        if (missionVariables.ups_dtimer)
        {
            // we need a delayed clock readout after a jump or launch.
            var message;
            if (clock.isAdjusting)
            {
                message = expandMissionText("ups_docs_small4", {ups_days_left: "updating...."});
            }
            else
            {
                message = expandMissionText("ups_docs_small4", {ups_days_left: this.getTimeToGo(missionVariables.ups_dtimer)});
                this.messagesTimer.stop();
            }
            mission.setInstructions(message);
        }
        else
        {
            // mission has ended
            mission.setInstructionsKey(null);
            this.messagesTimer.stop();
        }
    }
    
    this.addDocPirates = function (count)
    {
        system.addShips(this.pirate, count);
    	system.addShipsToRoute(this.pirate, count, 0.15, "wp");
    	system.addGroupToRoute("pirate", 5, Math.random(), "wp");
        var docpirates = system.shipsWithPrimaryRole("pirate");
        if (docpirates)
        {
            for (var i=0; i<docpirates.length; i++)
                if (docpirates[i].isValid && docpirates[i].AI === "pirateAI.plist")
                {
                    this.changePirateAI(docpirates[i], "upsSecDocPirateAI.plist", this.sendUpsDocMessage);
                    if (docpirates[i].bounty === 0) docpirates[i].bounty = Math.ceil(Math.random()*30+5);
                }
        }
    	if (this.ups_dcount > 15 && count > 5)
    	{
    		system.addShipsToRoute("ups_pirate_leader", 1, 0.35, "pw");
    		system.addShipsToRoute("ups_pirate_leader", 1, 0.85); // station route
    		system.addShipsToRoute("ups_pirate_leader", 1, 0.75, "pw");
    	}
    }
    
    this.changePirateAI = function (pirate, AI, message)
    {
        if (pirate && pirate.isValid)
        {
            pirate.switchAI(AI);
            pirate.script.sendUpsMessage = message; // message is the function to attach that sends the messages.
        }
    }
    
    this.playerEnteredNewGalaxy = function ()
    {
        // reset missions on galactic jump
        if (this.ups_docs != "SMALL_DELIVERY")
        {
            this.ups_docs = "NOT_NOW";
            mission.setInstructionsKey(null);
            var deletions = ["dplanetname", "docsbriefing", "dtimer", "docs_appearance", "docs_type", "chronicle_content"];
            for (var i=0; i<deletions.length; i++) delete missionVariables["ups_"+deletions[i]];
            delete this.ups_dplanet;
        }
        missionVariables.ups_galaxy = galaxyNumber;
    }
    
    this.setUpShips = function ()
    {
    	if (this.ups_docs === "SPECIAL_DELIVERY" && this.ups_dplanet === system.ID)
        {
    
            var dependancePos = Vector3D(0, 0, 2.6).fromCoordinateSystem("sps");
            var stationRole = (worldScripts["ups_sun"].ups_sun ? "ups_dependance2" : "ups_dependance");
            if (system.countShipsWithRole(stationRole) === 0)
            {
                system.addShips(stationRole, 1, dependancePos);
                system.addShips("ups-sunskim", 3, dependancePos);
                system.addShips("ups_shuttle", 8, dependancePos);
                worldScripts["ups_sun"].researcher_count = 0;
    			worldScripts["ups_sun"].sunShips("ups-sunskim", 3, "sp");
    			worldScripts["ups_sun"].sunShips(this.pirate, 2, "sp");
    			worldScripts["ups_sun"].sunShips("ups-sun-police", 3, "sp");
    			worldScripts["ups_sun"].sunShips("ups-sunskim", 4, "sw");
                if (worldScripts.transportSchedule)  // has Transports.OXP installed
                {
                    worldScripts["ups_sun"].sunShips("fueltransport", 3, "sp");
                }
            }
        }
    }
    
    this.setUpAsteroidMission = function ()
    {
        function locationAt (pos) {return basePosition.add(planetVector.multiply(pos));};
        var count = missionVariables.ups_asteroid_count;
        if (system.countShipsWithRole("ups-miner") === 0 && count > 8)
        {
            var basePosition = system.mainPlanet.position;
            var planetVector = Vector3D([0,0,1]); // unit vector pointing behind planet
            count = Math.ceil(count/2);
    
            system.addShips("asteroid", count, locationAt(180E3), 15E3);
            var specialAsteroids = system.addShips("asteroid", count, locationAt(225E3), 15E3);
            //add ups script to the spawned special asteroids. doing it this way allows asteroid storm scripts to run their spawn part that sets the color. does not work for the asteroid storm boulders though. I think we can live with it.
            for (var i = 0; i < specialAsteroids.length; i++) {
    			specialAsteroids[i].setScript("upsAsteroid.js");
    			//this is for the mission counter
                specialAsteroids[i].$upsAsteroid = true;
    		}
            system.addShips("asteroid", count, locationAt(270E3), 15E3);
            specialAsteroids = system.addShips("asteroid", count, locationAt(315E3), 15E3);
            //add ups script to the spawned special asteroids. doing it this way allows asteroid storm scripts to run their spawn part that sets the color. does not work for the asteroid storm boulders though. I think we can live with it.
            for (i = 0; i < specialAsteroids.length; i++) {
    			specialAsteroids[i].setScript("upsAsteroid.js");
    			//this is for the mission counter
    			specialAsteroids[i].$upsAsteroid = true;
    		}
            var miners = system.addShips("miner", 2, locationAt(270E3), 5E3);
            miners = miners.concat(system.addShips("miner", 5, locationAt(360E3)), 5E3);
            miners = miners.concat(system.addShips("miner", 5, locationAt(450E3)), 5E3);
    
            for (var i=0; i<miners.length; i++)
                if (miners[i].isValid)
                {
                    miners[i].switchAI("route1UpsMinerAI.plist");
                    miners[i].primaryRole = "ups-miner";
                }
        }
    }
    
    this.getTimeToGo = function (deadline)
    {
    	var timer = deadline - clock.minutes/24/60;
    	var days = Math.floor(timer)
    	var hours = Math.round((timer - days)*24)
    	var hoursText = ""
    	if (hours>1) hoursText = " " + hours +" hrs"; else if (hours==1) hoursText = " " + hours +" hr";
    	if (days > 1) return "within " + days + " days" + hoursText;
    	if (days === 1) return "within 1 day" + hoursText;
    	if (days === 0 && hours > 0) return "within" + hoursText;
    	if (days === 0) return "now";
    	return "by yesterday";
    }
    
    this.increaseReputation = function (reputation)
    {
            if (!this.ups_reputation) this.ups_reputation = 0
            this.ups_reputation += reputation;
            while (this.ups_reputation >=3)
            {
                player.increaseParcelReputation();
                this.ups_reputation -= 3;
            };
            while (this.ups_reputation <= -3)
            {
                player.decreaseParcelReputation();
                this.ups_reputation += 3;
            }
    }
    
    this._reputation = function ()
    {
        return player.parcelReputation;
    }
    
    this.launchEarthquakeRescue = function (count)
    {
        for (var i=0; i<count; i++)
        {
            var shuttle = player.ship.dockedStation.launchShipWithRole("shuttle");
            shuttle.displayName = shuttle.displayName + " (earthquake resque)";
            shuttle.switchAI("fallingShuttleAI.plist");
        }
    }
    
    this.upsMarkSystem = function (ID, oxp_name)
    {
        mission.markSystem({
                   system: ID,
                   name: oxp_name,
                   markerColor: "magentaColor",
                   markerShape: "MARKER_X"
        })
    }
    
    this.upsUnmarkSystem = function (ID, oxp_name)
    {
        mission.unmarkSystem({
                   system: ID,
                   name: oxp_name
        })
    }
    
    this.updateUpsMarkers = function ()
    {
        // one-time-only function to update pre-Oolite 1.77 markers to the new structure
        if (worldScripts["ups_docs"].ups_dplanet != null && worldScripts["ups_docs"].ups_dplanet >= 0)
        {
            if (["SPECIAL_DELIVERY", "CHRONICLE_DELIVERY", "EARTHQUAKE_DELIVERY"].indexOf(worldScripts["ups_docs"].ups_docs) > -1)
            {
                this.checkUpsMarker(worldScripts["ups_docs"].ups_dplanet, "ups_docs");
            }
        }
        if (worldScripts["ups_parcel"].ups_pplanet != null && worldScripts["ups_parcel"].ups_pplanet >= 0)
        {
            if (["SPECIAL_DELIVERY", "MEDICIN_DELIVERY", "MEDICIN_DELIVERY_2", "ASTRO_ACCEPTED"].indexOf(worldScripts["ups_parcel"].ups_parcel) > -1)
            {
                this.checkUpsMarker(worldScripts["ups_parcel"].ups_pplanet, "ups_parcel");
            }
        }
        if (worldScripts["ups_container"].ups_cplanet != null && worldScripts["ups_container"].ups_cplanet >= 0)
        {
            if (["SEARCHING", "SEARCHING2", "SEARCHING3"].indexOf(worldScripts["ups_container"].ups_container) > -1)
            {
                this.checkUpsMarker(worldScripts["ups_container"].ups_cplanet, "ups_container");
            }
        }
        if (worldScripts["ups_slaves"].ups_slplanet != null && worldScripts["ups_slaves"].ups_slplanet >= 0)
        {
            if (["HOMELAND", "FINAL"].indexOf(worldScripts["ups_slaves"].ups_slaves) > -1)
            {
                this.checkUpsMarker(worldScripts["ups_slaves"].ups_slplanet, "ups_slaves");
            }
        }
        if (worldScripts["ups_slaves"].ups_sl2planet != null && worldScripts["ups_slaves"].ups_sl2planet >= 0)
        {
            if (["RESCUE"].indexOf(worldScripts["ups_slaves"].ups_slaves) > -1)
            {
                this.checkUpsMarker(worldScripts["ups_slaves"].ups_sl2planet, "ups_slaves");
            }
        }
        if (worldScripts["ups_sun"].ups_splanet != null && worldScripts["ups_sun"].ups_splanet >= 0)
        {
            if (["SMALL_DELIVERY", "RESCUE"].indexOf(worldScripts["ups_sun"].ups_sun) > -1)
            {
                this.checkUpsMarker(worldScripts["ups_sun"].ups_splanet, "ups_sun");
            }
        }
    }
    
    this.checkUpsMarker = function (ID, oxp_name)
    {
        var markedSystems = mission.markedSystems;
        for (var i=0; i<markedSystems.length; i++)
        {
            if (markedSystems[i].system == ID && markedSystems[i].name == "__oolite_legacy_destinations")
            {
                mission.unmarkSystem(ID); // unmark legacy marker
                this.upsMarkSystem(ID, oxp_name);// add the new marker
            }
        }
    }
    
    // Code below is used by ship scripts
    this.messageTime = clock.absoluteSeconds // for use of spacing NPC messages by ship scripts.
    
    this.messageAllowed = function ()
    {
        if ((clock.absoluteSeconds - this.messageTime) > 5)
        {
            this.messageTime = clock.absoluteSeconds;
    		return true;
        }
    	else return false;
    }
    
    // Code below is linked to ship scripts
    this.sendUpsDocMessage = function ()
    {
    	if (worldScripts["ups_docs"].messageAllowed() && this.ship.target && this.ship.target.isPlayer)
    		this.ship.commsMessage(expandDescription("[ups_secret_talk]"))
        // no messages within 5 secs to avoid screen clutter.
    }
    
    this._overlay = function() {
        if (player.ship.hudHidden || player.ship.hudAllowsBigGui) {
            return {name:"upsnews-large.png", height:546};
        } else {
            return {name:"upsnews-small.png", height:546};
        }
    }
    
    this.cleanUpMarkers = function() {
        var ended = ["NO", "NOT_NOW"];
        var marked = mission.markedSystems;
        for (var i = marked.length - 1; i >= 0; i--) {
            if (marked[i].name === "ups_docs" && ended.indexOf(missionVariables.ups_docs) >= 0) mission.unmarkSystem({system:marked[i].system, name:"ups_docs"});
            if (marked[i].name === "ups_parcel" && ended.indexOf(missionVariables.ups_parcel) >= 0) mission.unmarkSystem({system:marked[i].system, name:"ups_parcel"});
            if (marked[i].name === "ups_container" && ended.indexOf(missionVariables.ups_container) >= 0) mission.unmarkSystem({system:marked[i].system, name:"ups_container"});
            if (marked[i].name === "ups_slaves" && ended.indexOf(missionVariables.ups_slaves) >= 0) mission.unmarkSystem({system:marked[i].system, name:"ups_slaves"});
        }
    }
    Scripts/ups_parcel.js
    "use strict";
    
    this.name        = "ups_parcel";
    this.author      = "eric walch";
    this.copyright   = "2008 eric walch.";
    this.license     = "CC BY-NC-SA 3.0";
    this.description = "Parcel missions of UPS Courier";
    
    this.startUp = function ()
    {
        this.pirate = "pirate";
        this.tiger =  (worldScripts.TigersTurf) ? "tigers" : this.pirate;
        this.dictatorsInstalled = (worldScripts.dictatorship_population !== undefined || worldScripts["dictators.js"]) ? true : false; // test for old legacy script and 1.4+ js script
        this.mark = false;
        this.upsMarkSystem = worldScripts["ups_docs"].upsMarkSystem;
        this.upsUnmarkSystem = worldScripts["ups_docs"].upsUnmarkSystem;
    }
    
    this.shipWillDockWithStation = function (station)
    {
        if (station.isMainStation)
        {
            this.ups_reports(station);
        }
    }
    
    this.missionScreenOpportunity = function ()
    {
        if (player.ship.docked)
        {
            this.missionOffers();
        }
    }
    
    this.ups_reports = function (station)
    {
        if (this.ups_parcel === "SMALL_DELIVERY" && system.government === 4 &&
                    this.ups_pplanet !==  system.ID && this.ups_pcount > 0 &&
                    missionVariables.ups_ambush != "YES")
        {
            this.reportDelivery(150);
        }
        if (this.ups_parcel === "MIDAIR_DELIVERY" && this.ups_pplanet ===  system.ID)
        {
            this.reportDelivery(150);
        }
    }
    
    this.addUpsContract = function ()
    {
        var thisSystem = system.info;
    
        var targetSystems = SystemInfo.filteredSystems(this, function(other){
               return (other.systemID !== thisSystem.systemID) && other.government == 4 && !system.sun.hasGoneNova;
        });
        var count = Math.ceil(Math.random()*5);
    
        for (var i = 0; i<count; i++)
        {
            var contractSystem = targetSystems[Math.floor(Math.random() * targetSystems.length)];
            var contractRoute = system.info.routeToSystem(contractSystem);
            if (contractRoute) {
    			var contractDescription = expandDescription("[ups_colors] [ups_parcel_description]");
    			contractDescription = contractDescription.charAt(0).toUpperCase() + contractDescription.slice(1);
    
    			var parcel =
    			{
    				destination: contractSystem.systemID,
    				sender: "UPS courier",
    				deadline: clock.seconds + contractRoute.time * 3600 + 3600*24*2,
    				payment: Math.floor(contractRoute.distance * (3+Math.random()) + (Math.pow(contractRoute.route.length,1+(0.3*player.parcelReputation))) + 50),
    				description: contractDescription,
    				route: contractRoute
    			}
    
    	        worldScripts["oolite-contracts-parcels"]._addParcelToSystem(parcel);
    		}
        }
    };
    
    this.reportDelivery = function (payment)
    {
        var message = expandDescription("You deliver the [mission_ups_parcel_appearance] parcels to the UPS office at the station. In return you receive "+payment+" ₢.");
        player.addMessageToArrivalReport(message);
        player.credits += payment;
        this.ups_pcount++;
        mission.setInstructionsKey(null);
        this.ups_parcel = "NOT_NOW";
        this.upsUnmarkSystem(this.ups_pplanet, this.name);
        missionVariables.ups_pplanetname = null;
        missionVariables.ups_ambush = null;
        missionVariables.ups_parcel_appearance = null;
        worldScripts["ups_docs"].increaseReputation(1);
    }
    
    this.guiScreenChanged = function (newScreen, oldScreen)
    {
        if (this.restartMissionOffer && newScreen !== "GUI_SCREEN_LONG_RANGE_CHART")
        {
            this.restartMissionOffer = false;
            if (guiScreen !== "GUI_SCREEN_MISSION") this.missionOffers();
        }
    }
    
    this.missionOffers = function ()
    {
        if (this._savedTargetSet) player.ship.targetSystem = this._savedTarget
        this._savedTarget = null;
        this._savedTargetSet = false;
    
    	var parcelStatus = this.ups_parcel // for speed reasons don't test to often against a mission variable.
        if (player.ship.dockedStation.isMainStation)
        {
            if (parcelStatus === "YES")
            {
                if (system.government === 4 && this.ups_pcount > 20 && player.ship.bounty < 3 &&
                            this._reputation() > 4 && (Math.random() < 0.25 || this.mark) && !worldScripts["ups_docs"].lookLongRangeChartScreen)
                {
                    // Medicine offer.
                    if (!this.mark)
                    {
                        // a nova system has the market set. Also avoid other systems with customised markets from selection.
                        this.ups_pplanet = Math.round(Math.random()*255);
                    }
    
    				missionVariables.ups_pplanetname = System.systemNameForID(this.ups_pplanet);
    				missionVariables.ups_parcel_appearance = expandDescription("[ups_numbers] [ups_colors] %I");
    				if (!this.mark) missionVariables.ups_disease = expandDescription("[ups_diseases]");
                    missionVariables.ups_medicine_inhabitants = System.infoForSystem(galaxyNumber, this.ups_pplanet).inhabitants;
                    var message = expandMissionText("ups_parcel_medicin_offer", {dockedStationName: player.ship.dockedStation.displayName});
    				mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", message: message,
                                    overlay: this._overlay(), choicesKey: "ups_parcel_accepted_yesnolook"},
                                    this.choiceEvaluation);
                    this.offering ="UPS_PARCEL_MEDICINS";
                    missionVariables.ups_medicine_inhabitants = null;
                }
                else if (system.government === 4 && worldScripts["ups_docs"].ups_dcount > 2 && player.ship.bounty < 3 && this._reputation() > 2)
                {
                    // Parcel offer
                    missionVariables.ups_parcel_appearance = expandDescription("[ups_numbers] [ups_colors] %I")
                    var today = new Date()
                    if (today.getMonth()==11 && today.getDate() > 15 && today.getDate() < 26)
                        missionVariables.ups_parcel_appearance += " Christmas";
                    if (!this.ups_pcount)
                        mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_parcel_1st_offer",
                                            overlay: this._overlay(), choicesKey: "ups_parcel_accepted_yesno"},
                                            this.choiceEvaluation)
                    else mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_parcel_offer",
                                            overlay: this._overlay(), choicesKey: "ups_parcel_accepted_yesno"},
                                            this.choiceEvaluation);
                    this.offering ="UPS_PARCELS";
                }
                else this.setUpsReputation();
            }
            if (parcelStatus === "SMALL_DELIVERY")
            {
                if (system.government === 4)
                {
                    if (this.ups_pplanet === system.ID)
                    {
                        this.ups_parcel = "ACCEPTED";
                        mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_back_at_home_parcels", overlay: this._overlay()});
                        worldScripts["ups_docs"].increaseReputation(-1);
                    }
                    else
                    {
                        this.ups_parcel = "NOT_NOW";
                        player.credits += 150;
                        mission.setInstructionsKey(null);
                        if (!this.ups_pcount)
                        {
                            mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_parcel_1st_unloading", overlay: this._overlay()});
                        }
                        else
                        {
                            if (missionVariables.ups_ambush === "YES")
                            {
                                mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_parcel_ambush", overlay: this._overlay()});
                                player.credits += 500;
                            }
                            // else // written in arrival report
                        }
                        this.ups_pcount++;
                        worldScripts["ups_docs"].increaseReputation(1);
                        missionVariables.ups_parcel_appearance = null;
                        missionVariables.ups_ambush = null;
                        missionVariables.ups_pplanetname = null;
                        if (!worldScripts.GNN && worldScripts.snoopers && worldScripts.snoopers.version >= 2.0) worldScripts.snoopers.insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_parcels]")});
                        if (worldScripts.GNN && worldScripts.GNN.version >= 1.0) worldScripts.GNN._insertNews({ID: this.name, Agency: 1, Message: expandDescription("[ups_snoopers_parcels]")});
                    }
                }
            }
            if (parcelStatus === "MEDICIN_DELIVERY")
            {
                var passedTime = clock.minutes/24/60 - this.ups_ptimerstart; // time in days.
                if (this.ups_pplanet === system.ID)
                {
                    missionVariables.ups_medicine_government = system.government;
                    missionVariables.ups_medicine_population = system.population;
                    missionVariables.ups_medicine_productivity = system.productivity;
                    missionVariables.ups_medicine_description = system.description;
                    missionVariables.ups_medicine_inhabitants = system.inhabitantsDescription;
                    mission.setInstructionsKey(null);
                    this.upsUnmarkSystem(this.ups_pplanet, this.name);
                    this.ups_ptimerstart = clock.days;
    
                    if (passedTime < 15)
                    {
                        this.ups_parcel = "MEDICIN_SUCCESS";
                        mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", messageKey: "ups_parcel_medicin_unloading", overlay: this._overlay()});
                        player.credits += 4500;
                        this.ups_pcount++;
                        worldScripts["ups_docs"].increaseReputation(4);
                    }
                    else
                    {
                        this.ups_parcel = "MEDICIN_FAILURE";
                        mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", messageKey: "ups_parcel_medicin_unloading_late", overlay: this._overlay()});
                        worldScripts["ups_docs"].increaseReputation(-3);
                        this.ups_ptimerstart = clock.days;
                    }
                }
                else if (passedTime > 30)
                {
                    this.ups_parcel = "NOT_NOW";
                    mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", messageKey: "ups_parcel_medicin_expired", overlay: this._overlay()});
                    mission.setInstructionsKey(null);
                    this.resetMedicines(galaxyNumber, false);
                    this.upsUnmarkSystem(this.ups_pplanet);
                    worldScripts["ups_docs"].increaseReputation(-6);
                }
                else
                {
                    this.updateMessages();
                }
            }
            if (parcelStatus === "MEDICIN_SUCCESS")
            {
                if (this.ups_pplanet != system.ID  && (clock.hours/24 - this.ups_ptimerstart) > 10)
                {
                    missionVariables.ups_parcel_appearance = expandDescription("[ups_numbers] [ups_colors] %I");
                    var message = expandMissionText("ups_parcel_medicin_offer_2a", {dockedStationName: player.ship.dockedStation.displayName});
                    mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", message: message,
                                        overlay: this._overlay(), choicesKey: "ups_parcel_accepted_yesno"},
                                        this.choiceEvaluation);
                    this.offering ="UPS_PARCEL_MEDICINS_2";
                }
                else this.setUpsReputation();
            }
            if (parcelStatus === "MEDICIN_FAILURE")
            {
                if (this.ups_pplanet != system.ID  && (clock.hours/24 - this.ups_ptimerstart) > 10)
                {
                    missionVariables.ups_parcel_appearance = expandDescription("[ups_numbers] [ups_colors] %I");
                    var message = expandMissionText("ups_parcel_medicin_offer_2b", {dockedStationName: player.ship.dockedStation.displayName});
                    mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", message: message,
                                        overlay: this._overlay(), choicesKey: "ups_parcel_accepted_yesno"},
                                        this.choiceEvaluation);
                    this.offering ="UPS_PARCEL_MEDICINS_2";
                }
                else this.setUpsReputation();
            }
            if (parcelStatus === "MEDICIN_DELIVERY_2" && this.ups_pplanet === system.ID)
            {
                this.ups_parcel = "NOT_NOW";
                var success = false;
                if ((clock.hours/24 - this.ups_ptimerstart) < 15)
                {
                    mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", messageKey: "ups_parcel_medicin_unloading_2", overlay: this._overlay()});
                    player.credits += 4500;
                    this.ups_pcount++;
                    worldScripts["ups_docs"].increaseReputation(4);
                    success = true;
                }
                else
                {
                    mission.runScreen({screenID: "ups-parcel", title: "Medicine Transport", messageKey: "ups_parcel_medicin_unloading_late", overlay: this._overlay()});
                    worldScripts["ups_docs"].increaseReputation(-5);
                }
    
                this.resetMedicines(galaxyNumber, success);
                this.upsUnmarkSystem(this.ups_pplanet, this.name);
                missionVariables.ups_pplanetname = null;
                missionVariables.ups_museumGoods = null;
                mission.setInstructionsKey(null);
            }
            if (parcelStatus === "LOGGING")
            {
                if (system.government === 4 && player.credits > 15000 && this.ups_pplanet != system.ID && (clock.days - this.ups_ptimerstart) > 8 && this._reputation() > 3 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
                {
                    if (!this.mark) //has not seen this offer before, aka not used the long range chart option.
                    {
                        missionVariables.ups_museumGoods = expandDescription("[ups_museumGoods]");
                        if (!missionVariables.ups_trumbles && missionVariables.trumbles === "TRUMBLE_BOUGHT" && Math.random() < 0.75 && player.trumbleCount === 0)
                        {
                            if (player.ship.equipmentStatus("EQ_TRUMBLE") === "EQUIPMENT_OK") player.ship.removeEquipment("EQ_TRUMBLE");
                            missionVariables.ups_museumGoods = expandDescription("[ups_trumbles]");
                            missionVariables.ups_trumbles = "ESCAPED";
                        }
                    }
                    var message = expandMissionText("ups_parcel_offer2", {dockedStationName: player.ship.dockedStation.displayName});
                    mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", message: message,
                                        overlay: this._overlay(), choicesKey: "ups_parcel_museum_accepted_yesnolook"},
                                        this.choiceEvaluation);
                    this.offering = "UPS_PARCELS2";
                }
                else this.setUpsReputation();
            }
            if (parcelStatus === "SPECIAL_DELIVERY")
            {
                if (this.ups_pplanet == system.ID)
                {
                    this.ups_parcel = "NOT_NOW";
                    if (missionVariables.ups_trumbles === "ESCAPED")
                    {
                        mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_parcel_trumbles", overlay: "trumblebox.png"});
                        player.credits -= 1000;
                        missionVariables.ups_trumbles = "YES";
    					this.playSound("trumblesqueal.ogg");
                    }
                    else
                    {
                        mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_parcel_unloading2", overlay: this._overlay()})
                    }
                    player.credits += 15000;
                    this.ups_pcount++;
                    mission.setInstructionsKey(null);
                    missionVariables.ups_pplanetname = null;
                    missionVariables.ups_museumGoods = null;
                    missionVariables.ups_escorts = null;
                    this.upsUnmarkSystem(this.ups_pplanet, this.name);
                    this.ups_ptimerstart = null;
                    worldScripts["ups_docs"].increaseReputation(3);
    				 // clean up escorts by docking them.
    				var escorts = system.shipsWithPrimaryRole("escort_ups", player.ship);
    				for (var i=0; i< escorts.length;i++) if (escorts[i].isValid) escorts[i].switchAI("dockingAI.plist");
                 }
            }
            if (parcelStatus === "ASTRO_OFFER" && system.government === 3 && system.economy < 3)
            {
                if (system.countShipsWithRole("astrofactory") > 0)
                {
                    missionVariables.ups_parcel_appearance = expandDescription("[ups_numbers] astro factory");
                    var message = expandMissionText("ups_parcel_astroAttack", {dockedStationName: player.ship.dockedStation.displayName});
                    mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", message: message, model: "astrofactory",
                                        overlay: this._overlay(), choicesKey: "ups_astro_parcel_accepted_yesno"},
                                        this.choiceEvaluation);
                    this.addAstroPirates(20);
                    this.offering = "UPS_PARCEL_ASTRO";
                }
                else parcelStatus = this.ups_parcel = "NO";
            }
            if (parcelStatus === "ASTRO_ACCEPTED" && system.countShipsWithRole("astrofactory") === 0)
            {
                this.ups_parcel = "NOT_NOW";
                if (this.ups_pplanet === system.ID)
                                mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_astro_parcel_destroyed", overlay: this._overlay()});
                mission.setInstructionsKey(null);
                this.upsUnmarkSystem(this.ups_pplanet, this.name);
                missionVariables.ups_pplanetname = null;
                missionVariables.ups_parcel_appearance = null;
                worldScripts["ups_docs"].increaseReputation(-2);
            }
            if (parcelStatus === "ASTRO_DELIVERY" && this.ups_pplanet === system.ID)
            {
                this.ups_parcel = "NOT_NOW";
                mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_astro_parcel_unloading", overlay: this._overlay()});
                player.credits += 250;
                this.ups_pcount++;
                mission.setInstructionsKey(null);
                this.upsUnmarkSystem(this.ups_pplanet, this.name);
                missionVariables.ups_pplanetname = null;
                missionVariables.ups_parcel_appearance = null;
                worldScripts["ups_docs"].increaseReputation(3);
            }
    		if (parcelStatus === "NO" || this.ups_parcel === "NOT_NOW")
    		{
                this.setUpsReputation();
    		}
        };
    	// Not at a mainStation
        if (parcelStatus === "ASTRO_ACCEPTED" && player.ship.dockedStation.name === "Imperial AstroFactory")
        {
            this.ups_parcel = "ASTRO_DELIVERY";
            mission.runScreen({screenID: "ups-parcel", title: "Parcel Transport", messageKey: "ups_astro_parcel_loading", overlay: this._overlay()});
            mission.setInstructionsKey("ups_astro_parcel_small2");
            var astroPirates = system.shipsWithPrimaryRole("pirate", player.ship, 40E3); // all pirates in a radius of 40000 m around the player
            for (var i=0; i<astroPirates.length;i++)
            {
                if (astroPirates[i].isValid)
                {
                    astroPirates[i].switchAI("upsAmbushPirateAI.plist");
                    if (astroPirates[i].script.name != "oolite-default-ship-script") astroPirates[i].setScript("oolite-default-ship-script.js");
                }
            }
            player.ship.fuel = 7;
        }
    }
    
    this.choiceEvaluation = function (choice)
    {
        switch (this.offering)
        {
            case "UPS_PARCELS":
            {
                if (choice === "YESParcel")
                {
                    mission.setInstructionsKey("ups_parcel_small");
                    this.ups_parcel = "ACCEPTED";
                    this.ups_pplanet = system.ID;
                    missionVariables.ups_pplanetname = system.name;
                    if (Math.random() < 0.2 && this.ups_pcount > 1) missionVariables.ups_ambush = "POSSIBLE"
                }
                else if (choice === "NOParcel")
                {
                    this.ups_parcel = "NOT_NOW";
                    missionVariables.ups_parcel_appearance = null;
                    worldScripts["ups_docs"].increaseReputation(-2);
                }
                break;
            }
            case "UPS_PARCELS2":
            {
                this.mark = false;
                if (choice === "YESParcel")
                {
                    mission.setInstructionsKey("ups_parcel_small2", "ups_parcel");
                    this.ups_parcel = "SPECIAL_DELIVERY";
                    this.upsMarkSystem(this.ups_pplanet, this.name);
                    player.credits -= 10000;
                    missionVariables.ups_escorts = 8;
                }
                else if (choice === "LOOKParcel")
                {
                    this.upsShowDestination(this.ups_pplanet);
                }
                else if (choice === "NOParcel")
                {
                    this.ups_parcel = "NOT_NOW";
                    worldScripts["ups_docs"].increaseReputation(-2);
                }
                break;
            }
            case "UPS_PARCEL_MEDICINS":
            {
                this.mark = false;
                if (choice === "YESParcel")
                {
                    mission.setInstructions(expandMissionText("ups_parcel_small3", {ups_days_left: "within 15 days"}));
                    this.ups_parcel = "MEDICIN_DELIVERY";
                    this.upsMarkSystem(this.ups_pplanet, this.name);
                    this.ups_ptimerstart = clock.minutes/24/60; // time in days
                    missionVariables.ups_museumGoods = missionVariables.ups_disease + " medicine";
                }
                else if (choice === "LOOKParcel")
                {
                    this.upsShowDestination(this.ups_pplanet);
                }
                else if (choice === "NOParcel")
                {
                    this.ups_parcel = "NOT_NOW";
                    worldScripts["ups_docs"].increaseReputation(-2);
                }
                break;
            }
            case "UPS_PARCEL_MEDICINS_2":
            {
                if (choice === "YESParcel")
                {
                    mission.setInstructions(expandMissionText("ups_parcel_small3", {ups_days_left: "within 15 days"}));
                    this.ups_parcel = "MEDICIN_DELIVERY_2";
                    this.upsMarkSystem(this.ups_pplanet, this.name);
                    this.ups_ptimerstart = clock.minutes/24/60;
                    var targetSystem = System.infoForSystem(galaxyNumber, this.ups_pplanet);
                    targetSystem.description = "The "+ missionVariables.ups_medicine_inhabitants+ " that are living on this planet are dying from an outbreak of " + missionVariables.ups_disease+".";
                    if (missionVariables.ups_medicine_inhabitants.indexOf("Very pale looking") < 0) // don't change this twice.
                        targetSystem.inhabitants = "Very pale looking " + missionVariables.ups_medicine_inhabitants;
                    targetSystem.population = Math.round(missionVariables.ups_medicine_population/10);
                    targetSystem.productivity = Math.round(missionVariables.ups_medicine_productivity/40);
                    targetSystem.government = 0;
                }
                else if (choice === "NOParcel")
                {
                    this.ups_parcel = "NOT_NOW";
                    worldScripts["ups_docs"].increaseReputation(-2);
                    this.resetMedicines(galaxyNumber, false);
                }
                break;
            }
            case "UPS_PARCEL_ASTRO":
            {
                if (choice === "YESParcel")
                {
                    mission.setInstructionsKey("ups_astro_parcel_small");
                    this.ups_parcel = "ASTRO_ACCEPTED";
                    missionVariables.ups_parcel_appearance = "imperial astrofactory";
                    this.ups_pplanet = system.ID;
                    missionVariables.ups_pplanetname = system.name;
                    this.upsMarkSystem(this.ups_pplanet, this.name);
                }
                else if (choice === "NOParcel")
                {
                    this.ups_parcel = "NOT_NOW";
                    worldScripts["ups_docs"].increaseReputation(-2);
                }
                break;
            }
            default: log(this.name, "Encountered an illegal mission offer: "+this.offering);
        }
        this.offering = null;
    }
    
    this.upsShowDestination = function (ID)
    {
        if (!this._savedTargetSet) this._savedTarget = player.ship.targetSystem;
        this._savedTargetSet = true;
        
        this.mark = true; // Keep this selected system.
        if (player.ship.equipmentStatus("EQ_ADVANCED_NAVIGATIONAL_ARRAY") == "EQUIPMENT_OK")
        {
            player.ship.targetSystem = ID;
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", backgroundSpecial: "LONG_RANGE_CHART_SHORTEST"});
        }
        else
        {
            mission.markSystem({
                       system: ID,
                       name: "ups_marker",
                       markerColor: "cyanColor",
                       markerScale: 2.0,
                       markerShape: "MARKER_DIAMOND"
            })
            var message = expandMissionText("ups_long_range_screen", {ups_system: System.systemNameForID(ID)});
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", message: message, backgroundSpecial: "LONG_RANGE_CHART"});
        }
    }
    
    this.addAstroPirates = function (amount)
    {
        var astroFactory = system.shipsWithPrimaryRole("astrofactory")[0];
        var astroPirates = system.addGroup("pirate", amount, astroFactory.position.add([0,20E3,0]), 5E3).ships;
        for (var i=0; i<amount;i++)
            if (astroPirates[i].isValid)
            {
                astroPirates[i].switchAI("upsAstroPirateAI.plist");
                if (astroPirates[i].script.name != "oolite-default-ship-script") astroPirates[i].setScript("oolite-default-ship-script.js");
                if (astroPirates[i].bounty === 0) astroPirates[i].bounty = Math.random()*100;
            }
    }
    
    this.shipExitedWitchspace = function ()
    {
        if (system.isInterstellarSpace) return;
    	var parcelStatus = this.ups_parcel;
        if (parcelStatus === "NOT_NOW" && Math.random() < 0.11) this.ups_parcel = "NO";
        if (parcelStatus === "NO")
        {
            if (Math.random() < 0.2 && system.government === 4)
            {this.ups_parcel = "YES"}
            else if (Math.random() < 0.05 && system.government === 4 && player.credits > 15000 && !system.sun.hasGoneNova && this.ups_pcount > 5)
            {
                this.ups_parcel = "LOGGING";
                this.ups_pplanet = system.ID;
                missionVariables.ups_pplanetname = system.name;
                this.ups_ptimerstart = clock.days;
            }
            else if (Math.random() < 0.1)
            {
                if (system.government > 3 && system.economy > 3 && missionVariables.ups_tcount > 1 && this._reputation() > 3)
                system.addShipsToRoute("derelict2_ptt", 1, Math.random(), "wp");
            }
            else if (missionVariables.ups_trumbles === "YES")
            {
                if (Math.random() < 0.85) this.playTrumble = new Timer(this, this.trumbleSound, 30+ Math.random()*120);
    			else
    			{
    				player.ship.awardEquipment("EQ_TRUMBLE");
    				missionVariables.ups_trumbles = "INFECTED";
                    // Variable stays "INFECTED" to prefent a second infection in this galaxy.
    			}
            }
            else if (Math.random() < 0.2 && this.dictatorsInstalled && system.government === 3 && system.economy < 3 && this.ups_pcount > 10)
            {
                this.ups_parcel = "ASTRO_OFFER";
            }
        }
        else
        {
            if (parcelStatus === "ACCEPTED"){parcelStatus = "SMALL_DELIVERY"; this.ups_parcel = parcelStatus; }
            if (parcelStatus === "SMALL_DELIVERY" && !system.isInterstellarSpace)
            {
                system.addShipsToRoute(this.pirate, 3, 0.33)
                if (Math.random() < 0.1 && missionVariables.ups_ambush === "POSSIBLE")
                {
    				missionVariables.ups_ambush = "YES";
    				player.consoleMessage(expandDescription("You are spotted with your [mission_ups_parcel_appearance] parcels."), 6)
    			}
    			if (missionVariables.ups_ambush === "YES" && Math.random() < 0.5)
    			{
    				system.addShips(this.tiger, 1);
    				system.addShipsToRoute(this.tiger, 2, 0.2, "wp");
    				if (player.score > 1000 && (Math.random()*100 + this.ups_pcount - 2*system.government) > 70)
    				{
    					system.addGroupToRoute(this.pirate, 3, 0, "wp");
    					system.addGroup(this.tiger, 3, this.randomPositionOnRoute(), 5E3);
    					system.addShipsToRoute("ups_pirate_leader", 1, Math.random(), "wp");
    				}
    				if (player.score > 2500 && (Math.random()*100 + this.ups_pcount - 2*system.government) > 70)
    				{
    					system.addGroupToRoute(this.tiger, 3, 0, "wp");
    					system.addGroup(this.tiger, 3, this.randomPositionOnRoute(), 5E3);
    					system.addShipsToRoute("ups_pirate_leader", 1, Math.random(), "wp");
    					if (worldScripts.hardpirates1 !== undefined)
    					{
    						var hardpirates = system.addShips("hardpirates", 1);
                            this.changePirateAI(hardpirates[0], "upsAmbushPirateAI.plist", this.sendUpsAmbushMessage);
    					}
    				}
    				var ambushpirates = system.shipsWithPrimaryRole("pirate", player.ship);
    				for (var i=0; i<ambushpirates.length;i++)
                    {
    					if (ambushpirates[i].isValid) //could have already been destroyed or not present
    					{
    						this.changePirateAI(ambushpirates[i], "upsAmbushPirateAI.plist", this.sendUpsAmbushMessage);
                            if (player.score > 2500 * Math.random()) this.awardShields(ambushpirates[i], "EQ_SHIELD_BOOSTER");
                            if (player.score > 4500 * Math.random()) this.awardShields(ambushpirates[i], "EQ_SHIELD_ENHANCER");
    					}
                    }
    			}
            }
            if (parcelStatus === "SPECIAL_DELIVERY")
            {
                if (system.countShipsWithPrimaryRole("escort_ups") == 0 && missionVariables.ups_escorts > 0)
                                            system.addShips("escort_ups", missionVariables.ups_escorts, player.ship.position, 10000);
                if (Math.random() < 0.5)
                {
                    system.addGroupToRoute(this.pirate, 4, 0.05, "wp");
                    system.addGroupToRoute(this.pirate, 3, 0.075, "wp");
                    system.addGroupToRoute(this.pirate, 3, 0.10, "wp");
                    system.addGroupToRoute(this.pirate, 2, 0.125, "wp");
                    system.addShipsToRoute(this.pirate, 1, 0.15, "wp");
                    var parcelpirates1 = system.shipsWithPrimaryRole(this.pirate, player.ship);
                    for (var i=0; i<13;i++) this.changePirateAI(parcelpirates1[i], "upsParcelPirateAI.plist", this.sendUpsParcelMessage);
                }
                if (Math.random() < 0.25)
                {
    				var parcelpirates2 = system.addGroupToRoute(this.tiger, 5, 0, "wp");
    				for (var i=0; i<5;i++) this.changePirateAI(parcelpirates2.ships[i], "upsParcelPirateAI.plist", this.sendUpsParcelMessage);
                }
    			this.addParcelPirates("pirate", 5);
    			if (missionVariables.ups_trumbles && missionVariables.ups_trumbles !== "INFECTED" && Math.random() < 0.50)
                {
                    this.playTrumble = new Timer(this, this.trumbleSound, 30+ Math.random()*120);
                }
            }
            if (parcelStatus === "MEDICIN_DELIVERY")
    		{
    			if (Math.random() < 0.25) this.addParcelPirates("pirate", 5);
                this.updateMessages();
    		}
            if (parcelStatus === "MEDICIN_DELIVERY_2")
    		{
    			if (Math.random() < 0.25) system.addGroup(this.tiger, 3, this.randomPositionOnRoute(), 5E3);
    			if (Math.random() < 0.25) system.addGroup(this.tiger, 3, this.randomPositionOnRoute(), 5E3);
    			if (Math.random() < 0.5) this.addParcelPirates("pirate", Math.ceil(Math.random()*5));
    
                this.updateMessages();
    		}
    		if (parcelStatus === "ASTRO_ACCEPTED")
    		{
    			mission.setInstructionsKey(null);
    			this.ups_parcel = "NOT_NOW";
    			this.upsUnmarkSystem(this.ups_pplanet);
    			missionVariables.ups_pplanetname = null;
    			missionVariables.ups_parcel_appearance = null;
    		}
        }
        if (system.government === 4 && !system.sun.hasGoneNova && system.mainStation)
        {
            this.addUpsContract();
        }
    }
    
    this.updateMessages = function ()
    {
        if (this.messagesTimer)
        {
            this.messagesTimer.start();
        }
        else
        {
            this.messagesTimer = new Timer(this, this.delayedMessageUpdate, 0, 2); // clock might be not valid yet.
        }
    }
    
    this.delayedMessageUpdate = function delayedMessageUpdate()
    {
        // we need a delayed clock readout after a jump.
        if (clock.isAdjusting)
        {
            mission.setInstructions(expandMissionText("ups_parcel_small3", {ups_days_left: "updating...."}));
        }
        else
        {
            mission.setInstructions(expandMissionText("ups_parcel_small3",
                        {ups_days_left: worldScripts["ups_docs"].getTimeToGo(this.ups_ptimerstart + 15)}));
            this.messagesTimer.stop();
        }
    }
    
    this.addParcelPirates = function (role, count)
    {
        var parcelpirates = system.addGroup(role, count, this.randomPositionOnRoute(), 5E3);
        if (parcelpirates)
        {
            parcelpirates.name = "parcelpirates";
            for (var i=0; i<parcelpirates.count; i++)
            {
                this.changePirateAI(parcelpirates.ships[i], "upsParcelPirateAI.plist", this.sendUpsParcelMessage);
                if (parcelpirates.ships[i].isValid)
                {
                    this.awardShields(parcelpirates.ships[i], "EQ_SHIELD_ENHANCER");
                }
            }
        }
    }
    
    this.changePirateAI = function (pirate, AI, message)
    {
        if (pirate && pirate.isValid)
        {
            pirate.switchAI(AI);
            // if (pirate.script.name != "oolite-default-ship-script") pirate.setScript("oolite-default-ship-script.js"); // should we reset this?
            pirate.script.sendUpsMessage = message; // message is the function to attach that sends the messages.
        }
    }
    
    this.randomPositionOnRoute = function ()
    {
        var end = system.mainPlanet.position;
        end.z -= 2 * system.mainPlanet.radius;
        return Vector3D.interpolate([0,0,0], end, Math.random()).add(Vector3D.randomDirectionAndLength(20E3));
    };
    
    this.awardShields = function (ship, equipment)
    {
        if (ship.equipmentStatus(equipment) === "EQUIPMENT_UNAVAILABLE")
        {
            ship.awardEquipment(equipment);
            ship.energy = ship.maxEnergy;
        }
    }
    
    this.playerEnteredNewGalaxy = function (galaxy)
    {
        // reset missions on galactic jump
    	if (--galaxy < 0) galaxy = 8;
    	if (this.ups_parcel.indexOf("MEDICINE") > -1) this.resetMedicines(galaxy, false);
        if (this.ups_parcel != "SMALL_DELIVERY")
        {
                missionVariables.ups_pplanetname = null;
    			missionVariables.ups_parcel_appearance = null;
                mission.setInstructionsKey(null);
                this.ups_pplanet = null;
                this.ups_parcel = "NOT_NOW";
                this.ups_ptimerstart = null;
        }
        if (player.ship.equipmentStatus("EQ_TRUMBLE") !== "EQUIPMENT_OK") delete missionVariables.ups_trumbles;
    }
    
    this.trumbleSound = function trumbleSound()
    {
    	this.playSound("trumblesqueal.ogg")
    }
    
    this.playSound = function (song)
    {
    	if (!this.mySound) this.mySound = new SoundSource;
    	this.mySound.sound = song; // this.playSound("trumblesqueal.ogg");
    	this.mySound.play();
    }
    
    this.resetMedicines = function (galaxy, success)
    {
        var targetSystem = System.infoForSystem(galaxy, this.ups_pplanet);
    	if (missionVariables.ups_medicine_government)
    	{
    		missionVariables.ups_medicine_description = null;
    		missionVariables.ups_medicine_inhabitants = null;
    		missionVariables.ups_medicine_population = null;
    		missionVariables.ups_medicine_government = null;
    		missionVariables.ups_medicine_productivity = null;
            if (success)
            {
                // reset descriptions for a successful mission.
                targetSystem.description = null;
                targetSystem.inhabitants = null;
                targetSystem.population = null;
                targetSystem.government = null;
                targetSystem.productivity = null;
            }
    		// targetSystem.market = null; // not possible to read correctly in 1.74 ?
    	}
    	missionVariables.ups_disease = null;
    	missionVariables.ups_museumGoods = null;
        this.ups_ptimerstart = null;
    }
    
    this._reputation = function ()
    {
        return player.parcelReputation;
    }
    
    this.setUpsReputation = function ()
    {
        if (system.government <= 1) return; // No update in anarchy and feudal!
    
        var message;
        switch (this._reputation())
        {
            case 7: message = "outstanding"; break;
            case 6: message = "excellent"; break;
            case 5: message = "dedicated"; break;
            case 4: message = "very good"; break;
            case 3: message = "good"; break;
            case 2: message = "above average"; break;
            case 1: message = "average"; break;
            case 0: message = "rookie"; break;
            case -1: message = "incompetent"; break;
            case -2: message = "unreliable"; break;
            case -3: message = "irresponsible"; break;
            default: message = "not trustworthy"; // for all other negative numbers
        }
        mission.setInstructions(expandMissionText("ups_parcel_reputation", {ups_reputation: message}));
    }
    
    // Code below is linked to ship scripts
    this.sendUpsParcelMessage = function ()
    {
    	if (worldScripts["ups_docs"].messageAllowed() && this.ship.target && this.ship.target.isPlayer)
    		this.ship.commsMessage(expandDescription("[ups_parceltalk]"));
        // no messages within 5 secs to avoid screen clutter.
    }
    
    this.sendUpsAmbushMessage = function ()
    {
    	if (worldScripts["ups_docs"].messageAllowed() && this.ship.target && this.ship.target.isPlayer)
    		this.ship.commsMessage(expandDescription("[ups_ambush_talk]"));
        // no messages within 5 secs to avoid screen clutter.
    }
    
    this._overlay = function() {
        if (player.ship.hudHidden || player.ship.hudAllowsBigGui) {
            return {name:"upsnews-large.png", height:546};
        } else {
            return {name:"upsnews-small.png", height:546};
        }
    }
    
    Scripts/ups_slaves.js
    "use strict";
    
    this.name        = "ups_slaves";
    this.author      = "eric walch";
    this.copyright   = "2008 eric walch.";
    this.license     = "CC BY-NC-SA 3.0";
    this.description = "Slaves missions of UPS Courier";
    
    this.startUp = function ()
    {
        this.mark = false;
        this.upsMarkSystem = worldScripts["ups_docs"].upsMarkSystem;
        this.upsUnmarkSystem = worldScripts["ups_docs"].upsUnmarkSystem;
    }
    
    this.startUpComplete = function() 
    {
        // fixing issue where ups_slaves hasn't been initialised properly.
        //if (this.ups_slaves == null) this.ups_slaves = "NO";
    }
    
    this.shipLaunchedFromStation = function ()
    {
      if (this.ups_slaverescue)
      {
        this.setUpShips();
      }
    }
    
    this.missionScreenOpportunity = function ()
    {
        // this.ups_slaverescue is set to "true" by a scooping a special escape_pod generated by a cobraIII.
        if (player.ship.docked && this.ups_slaverescue)
        {
            this.missionOffers();
        }
    }
    
    this.guiScreenChanged = function (newScreen, oldScreen)
    {
        if (this.restartMissionOffer && newScreen !== "GUI_SCREEN_LONG_RANGE_CHART")
        {
            this.restartMissionOffer = false;
            if (guiScreen !== "GUI_SCREEN_MISSION") this.missionOffers();
        }
    }
    
    this.missionOffers = function ()
    {
        if (this._savedTargetSet) player.ship.targetSystem = this._savedTarget
        this._savedTarget = null;
        this._savedTargetSet = false;
    
    	var slaveStatus = this.ups_slaves;
        if (player.ship.dockedStation.isMainStation)
        {
            if (slaveStatus === "SLAVE" && !worldScripts["ups_docs"].lookLongRangeChartScreen)
            {
                var message = expandMissionText("ups_slave_homeland", {ups_slavename: this.ups_slavename});
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message,
                                    overlay: this._overlay(), choicesKey: "ups_slave_accepted_yesnolook"},
                                    this.choiceEvaluation);
                this.offering ="UPS_SLAVES_HOMELANDS";
            }
            if (slaveStatus === "HOMELAND" && this.ups_slplanet === system.ID)
            {
                this.ups_slaves = "NOT_NOW";
                this.upsUnmarkSystem(this.ups_slplanet, this.name);
                var message = expandMissionText("ups_slave_homeland2", {ups_slavename: this.ups_slavename});
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message, overlay: this._overlay()});
                mission.setInstructionsKey(null);
                player.ship.manifest["gem_stones"] += 15;
                this.ups_slcount++;
                player.increasePassengerReputation();
            }
            if (slaveStatus === "RESCUEOFFER" && system.government > 3 && system.economy < 3 && !worldScripts["ups_docs"].lookLongRangeChartScreen)
            {
                var message = expandMissionText("ups_slave_rescueoffer", {ups_slavename: this.ups_slavename});
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message,
                                overlay: this._overlay(), choicesKey: "ups_slave_accepted2_yesnolook"},
                                this.choiceEvaluation);
                this.offering ="UPS_SLAVES_RESCUE";
            }
            if (slaveStatus === "RESCUE" && system.ID === this.ups_sl2planet)
            {
    			if (this.ups_convoy === "KILLED")
    			{
    				var key;
                    if (this.ups_sl2count > 10)
    				{
    					// rescued all
                        key = "ups_slave_rescue10";
                        player.ship.manifest["gem_stones"] += 50;
    				}
    				else
    				{
    					if (this.ups_sl2count > 0) key = "ups_slave_rescue"
    					else key = "ups_slave_rescue0"; // rescued none!
    				}
                    var message = expandMissionText(key, {ups_slavesrescued: this.ups_sl2count, ups_slavename: this.ups_slavename});
                    mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message});
    				this.resetConvoyMission();
    				this.ups_slcount += 3;
    			}
    			else if (this.ups_convoy === "ADDED" && system.countShipsWithRole("ups-slaveconvoy") === 0)
    			{
                    var message = expandMissionText("ups_slave_rescue_failure", {ups_slavename: this.ups_slavename});
    				mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message});
    				this.resetConvoyMission();
    				this.ups_slcount -= 2;
    			};
            }
            if (slaveStatus === "FINALOFFER" && system.government > 2)
            {
                var message = expandMissionText("ups_slave_final", {ups_slavename: this.ups_slavename});
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message,
                                choicesKey: "ups_slave_accepted_yesno"}, this.choiceEvaluation);
                this.offering = "UPS_SLAVES_FINAL";
            }
            if (slaveStatus === "FINAL" && this.ups_slplanet === system.ID)
            {
                this.ups_slaves = "GROUNDBATTLE";
                var message = expandMissionText("ups_slave_groundbattle", {ups_slavename: this.ups_slavename});
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message, model: "ups_slave_satellite", spinModel: false});
                this.$startCallback();
                mission.setInstructionsKey("ups_slave_groundbattle_small");
                this.ups_slavesrescued = 5;
            }
            if (slaveStatus === "DEBRIEFING" && this.ups_slplanet === system.ID)
            {
                this.ups_slaves = "NOT_NOW";
                mission.setInstructionsKey(null);
                var key;
                if (this.ups_slavesrescued == 5)
                {
                    key = "ups_slave_debriefing"; // rescued all
                    player.credits += 6000;
                }
                else
                {
                    if (!this.ups_slavesrescued)
                    {
                        key = "ups_slave_debriefing2"; // rescued none
                    }
                    else
                    {
                        key = "ups_slave_debriefing1";
                        player.credits += 4000;
                    }
                }
                var message = expandMissionText(key, {ups_slavesrescued: this.ups_slavesrescued});
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", message: message});
                mission.setInstructionsKey(null);
    			this.upsUnmarkSystem(this.ups_slplanet, this.name);
                this.resetSlaves(galaxyNumber);
                return;
            }
        }
        if (player.ship.dockedStation.name === "Ground Radar")
        {
            if (slaveStatus === "DEBRIEFING" || slaveStatus === "GROUNDBATTLE")
            {
                this.ups_slaves = "FAILED";
                mission.runScreen({screenID: "ups-slaves", title: "Slave Mission", messageKey: "ups_slave_trapped"});
    			this.upsUnmarkSystem(this.ups_slplanet, this.name)
                this.resetSlaves(galaxyNumber);
                // should this end the game now?
            }
        }
    }
    
    this.choiceEvaluation = function (choice)
    {
        switch (this.offering)
        {
            case "UPS_SLAVES_HOMELANDS":
            {
                this.mark = false
                if (choice === "YESSlave")
                {
                    this.upsMarkSystem(this.ups_slplanet, this.name);
                    mission.setInstructions(expandMissionText("ups_slave_small", {ups_slavename: this.ups_slavename}));
                    this.ups_slaves = "HOMELAND";
                }
                else if (choice === "LOOKSlave")
                {
                    this.upsShowDestination(this.ups_slplanet);
                }
                else if (choice === "NOSlave")
                {
                    this.ups_slaves = "NOT_NOW";
                    if (Math.random()<0.5) player.decreasePassengerReputation();
                }
                break;
            }
    
            case "UPS_SLAVES_RESCUE":
            {
                if (choice === "YESSlave")
                {
                    this.upsMarkSystem(this.ups_sl2planet, this.name);
                    mission.setInstructionsKey("ups_slave_small3");
                    this.ups_slaves = "RESCUE";
                    this.ups_sl2count = 0;
                    this.ups_convoy = null;
                }
                else if (choice === "LOOKSlave")
                {
                    this.upsShowDestination(this.ups_sl2planet);
                }
                else if (choice === "NOSlave")
                {
                    this.ups_slaves = "NOT_NOW";
                }
                break;
            }
    
            case "UPS_SLAVES_FINAL":
            {
                if (choice === "YESSlave")
                {
                    this.upsMarkSystem(this.ups_slplanet, this.name);
                    mission.setInstructions(expandMissionText("ups_slave_small2", {ups_slavename: this.ups_slavename}));
                    this.ups_slaves = "FINAL";
                }
                else if (choice === "NOSlave")
                {
                    this.ups_slaves = "NOT_NOW";
                }
                break;
            }
            default: log(this.name, "Encountered an illegal mission offer: "+this.offering);
        }
        this.offering = null;
    }
    
    this.upsShowDestination = function (ID)
    {
        if (!this._savedTargetSet) this._savedTarget = player.ship.targetSystem;
        this._savedTargetSet = true;
        
        this.mark = true; // Keep this selected system.
        if (player.ship.equipmentStatus("EQ_ADVANCED_NAVIGATIONAL_ARRAY") == "EQUIPMENT_OK")
        {
            player.ship.targetSystem = ID;
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", backgroundSpecial: "LONG_RANGE_CHART_SHORTEST"});
        }
        else
        {
            mission.markSystem({
                system: ID,
                name: "ups_marker",
                markerColor: "cyanColor",
                markerScale: 2.0,
                markerShape: "MARKER_DIAMOND"
            })
            var message = expandMissionText("ups_long_range_screen", {ups_system: System.systemNameForID(ID)});
            mission.runScreen({title: "Long Range Chart", screenID:"ups-map", message: message, backgroundSpecial: "LONG_RANGE_CHART"});
        }
    }
    
    this.setUpShips = function ()
    {
        if (this.ups_slaves === "GROUNDBATTLE")
        {
            if (system.ID === this.ups_slplanet && system.countShipsWithRole("ups_slave_satellite") === 0)
            {
                //system.legacy_addShipsAtPrecisely("ups_slave_satellite", 1, "pwp", [0, 0, 1.004]);
                //system.legacy_addShipsAtPrecisely("ups_slave_satellite", 1, "pwp", [0, 0.05, 1.004]);
                //system.legacy_addShipsAtPrecisely("ups_slave_satellite", 1, "pwp", [0.05, 0, 1.004]);
                //system.legacy_addShipsAtPrecisely("ups_slave_satellite", 1, "pwp", [0.05, 0.05, 1.004]);
                system.addShips("ups_slave_satellite", 1, Vector3D(0, 0, 1.008).fromCoordinateSystem("pwp"), 1);
                system.addShips("ups_slave_satellite", 1, Vector3D(0, 0.05, 1.008).fromCoordinateSystem("pwp"), 1);
                system.addShips("ups_slave_satellite", 1, Vector3D(0.05, 0, 1.008).fromCoordinateSystem("pwp"), 1);
                system.addShips("ups_slave_satellite", 1, Vector3D(0.05, 0.05, 1.008).fromCoordinateSystem("pwp"), 1);
                this.ups_satellitecount = 4;
                // Smallest planet radius = 30 000 meters. 0.004*30 000 = 120 meters. This just fits for the towers.
                // phkb: adjusted to 0.008, as the planet is rotating and the radars are not being moved with the planet
                //       so having them slightly more above the surface reduces the break in mimesis
            }
        }
        if (this.ups_slaves === "RESCUE")
        {
            if (system.ID === this.ups_sl2planet && system.countShipsWithRole("ups-slaveconvoy") === 0 && !this.ups_convoy)
            {
                log(this.name, "adding ups-convoy-l to system");
                system.addShipsToRoute("ups-convoy-l", 1, 0.5, "wp");
    			this.ups_convoy = "ADDED";
            }
        }
    }
    
    this.shipExitedWitchspace = function ()
    {
        if (system.isInterstellarSpace) return;
        if (this.ups_slaverescue)
        {
            this.setUpShips();
            if (!this.ups_slaves)
            { // selection of a target system for all slave missions. And make sure you don't select a Nova system.
                if (system.government === 2 && system.economy > 5 && !system.sun.hasGoneNova && !this.ups_splanet)
                // original had ' && (!system.info.hasOwnProperty("market") || !system.info.market)' in above clause
                // replaced with " && !this.ups_splanet"
                // even this clause may not be required
                {
                    this.ups_slaves = "NO";
                    this.ups_slplanet = system.ID;
                    missionVariables.ups_slplanetname = system.name;
                    this.ups_slcount = 0;
                    missionVariables.ups_slplanetdescription = system.description;
                    system.description += " Currently this planet is in civil war and is selling a lot of its inhabitants as slaves.";
                    //system.info.market = "ups_slaveplanet";
                    // There are a couple of options here to do the update of the slave market.
                    // We could create an updateGeneralCommodityDefinition or updateLocalCommodityDefinition routine
                    // but the downside is that we would have to change the logic for the decision making process,
                    // changing the time the decision is made to be before we exit witchspace.
                    // To keep the logic as similar to the original as possible, we're instead going to force an
                    // update to market prices and quantities after the system is generated.
                    // We're assuming that the only thing that needs to be adjusted is the "slaves" price and quantity,
                    // and that all other trade goods remain the same.
                    this._updateLocalSlaveMarket();
                    // we should only have to do this once, as the adjusted definition will be saved and restored
                    // as part of a player save game.
                }
            }
            var slaveStatus = this.ups_slaves;
            if (slaveStatus === "NOT_NOW" && Math.random() < 0.11) this.ups_slaves = "NO";
            if (slaveStatus === "NO")
            {
                if (this.ups_slcount > 5 && system.government > 2 && Math.random() > 0.9)
                {
                    this.ups_slaves = "FINALOFFER";
                }
                else if (this.ups_slcount > 1 && Math.random() > 0.9 && system.government === 2 && !system.sun.hasGoneNova)
                {
                    this.ups_slaves = "RESCUEOFFER";
                    this.ups_sl2planet = system.ID;
                    missionVariables.ups_sl2planetname = system.name;
                }
                else if (!system.isInterstellarSpace && Math.random() > 0.75 && system.economy < 3)
                {
                    log(this.name, "adding ups-slavecobra ship to system");
                    system.addShipsToRoute("ups-slavecobra", 1, Math.random() * 0.8 + 0.1); // station route
                }
            }
        }
        else if (system.government === 2 && Math.random() > 0.5)
        {
            log(this.name, "adding ups_slave_start ship to system");
            system.addShipsToRoute("ups_slave_start", 1, Math.random(), "wp");
        }
    }
    
    this.resetConvoyMission = function ()
    {
        this.ups_sl2count = null;
        this.upsUnmarkSystem(this.ups_sl2planet, this.name);
        this.ups_sl2planet = null;
        missionVariables.ups_sl2planetname = null;
        this.ups_convoy = null;
        mission.setInstructionsKey(null);
        this.ups_slaves = "NOT_NOW";
    }
    
    this.resetSlaves = function (galaxy)
    {
        // do some cleanup of variables
        mission.setInstructionsKey(null)
    	if (missionVariables.ups_slplanetdescription)
    	{
    		System.infoForSystem(galaxy, this.ups_slplanet).description = null;
            /*if (System.infoForSystem(galaxy, this.ups_slplanet).hasOwnProperty("market"))
            {
                System.infoForSystem(galaxy, this.ups_slplanet).market = null;
            }*/
    		missionVariables.ups_slplanetdescription = null;
    	}
        var deletions = ["slavename", "slplanetname", "sl2count", "sl2planetname"];
        for (var i = 0; i < deletions.length; i++) delete missionVariables["ups_" + deletions[i]];
        var deletions2 = ["slaves", "slaverescue", "slplanet", "sl2planet", "slcount", "slavesrescued"];
        for (var i = 0; i < deletions2.length; i++) this["ups_" + deletions2[i]] = null;
    }
    
    this.playerEnteredNewGalaxy = function (galaxy)
    {
    	if (this.ups_slaves)
    	{
    		var oldGalaxy = --galaxy;
    		if (oldGalaxy < 0) oldGalaxy = 8;
    		this.resetSlaves(oldGalaxy);
    	}
    }
    
    this.$startCallback = function ()
    {
        if (!isValidFrameCallback(this.$callbackID)) this.$callbackID = addFrameCallback(this.rotateDisplayModel.bind(this));
        this.displayModel = mission.displayModel;
    }
    
    this.rotateDisplayModel = function (delta)
    {
        if (guiScreen != "GUI_SCREEN_MISSION" || !mission.displayModel || this.displayModel != mission.displayModel)
        {
            // we are finisched, or the missionscreen has changed.
            this.$cleanupCallback();
            return;
        };
        var q = new Quaternion(1,0,0,0);
        q = q.rotateY(0.1 * delta);
        mission.displayModel.orientation = mission.displayModel.orientation.multiply(q);
    
    }
    
    this.$cleanupCallback = function ()
    {
    	if (this.$callbackID && isValidFrameCallback(this.$callbackID))
    	{
    		removeFrameCallback(this.$callbackID);
            delete this.$callbackID;
    	}
    }
    
    this._overlay = function() {
        if (player.ship.hudHidden || player.ship.hudAllowsBigGui) {
            return {name:"upsnews-large.png", height:546};
        } else {
            return {name:"upsnews-small.png", height:546};
        }
    }
    
    this._updateLocalSlaveMarket = function() {
        var oldDefs = [0, 0, 52, -3, -5, 8, 7, 31, 0 ];
        var market_base_price = oldDefs[2];
        var market_eco_adjust_price = oldDefs[3];
        var market_eco_adjust_quantity = oldDefs[4];
        var market_base_quantity = oldDefs[5];
        var market_mask_price = oldDefs[6];
        var market_mask_quantity = oldDefs[7];
        var market_rnd = Math.floor(Math.random() * 256);
    
        var economy = system.economy;
    
        var price = (market_base_price + (market_rnd & market_mask_price) + (economy * market_eco_adjust_price)) & 255;
        price *= 0.4;
    
        var quantity = (market_base_quantity + (market_rnd & market_mask_quantity) - (economy * market_eco_adjust_quantity)) & 255;
        if (quantity > 127) quantity = 0;
        quantity &= 63;
    
        system.mainStation.setMarketPrice("slaves", price * 10);
        system.mainStation.setMarketQuantity("slaves", quantity);
    }
    Scripts/ups_sun.js
    "use strict";
    
    this.name        = "ups_sun";
    this.author      = "eric walch";
    this.copyright   = "2008 eric walch.";
    this.license     = "CC BY-NC-SA 3.0";
    this.description = "Solar missions of UPS Courier";
    
    // TODO: move station creation into systemWillPopulate
    
    this.startUp = function ()
    {
    	this.firstLaunch = true;
        this.upsMarkSystem = worldScripts["ups_docs"].upsMarkSystem;
        this.upsUnmarkSystem = worldScripts["ups_docs"].upsUnmarkSystem;
    }
    
    this.initSunMission = function (planetName, planetID)
    {
        // initialise the sun missions. Called by an ups_doc mission.
        worldScripts["ups_sun"].ups_sun = "NOT_NOW";
        worldScripts["ups_sun"].ups_sunbase = true;
        worldScripts["ups_sun"].ups_sunvisits = 0;
        worldScripts["ups_sun"].ups_splanetname = planetName;
        worldScripts["ups_sun"].ups_splanet = planetID;
    }
    
    this.playerEnteredNewGalaxy = function ()
    {
        if (this.ups_sun) this.sunReset(); // reset everything in a new galaxy.
    }
    
    this.shipLaunchedFromStation = function ()
    {
        if (this.ups_sunbase)
        {
            if (this.firstLaunch) {this.setUpShips(); this.firstLaunch = false};
            if (this.ups_blackbox === "YES" && this.researcher_count > 0)
            {
                    this.ups_blackbox = "SEARCHING";
                    this.ups_s2count = 0;
                    this.ups_s2award = 0;
                    system.addShips("UPS-black_box", this.researcher_count, Vector3D(0, 0, 1.1).fromCoordinateSystem("sps"));
                    this.researcher_count = 0; // reset counter
            }
        }
    }
    
    this.missionScreenOpportunity = function ()
    {
        // this.ups_sunbase is set to 'true' by a mission in ups_docs.
        if (player.ship.docked && this.ups_sunbase)
        {
            this.missionOffers();
        }
    }
    
    this.missionOffers = function ()
    {
    	var sunStatus = this.ups_sun;
        if (player.ship.dockedStation.isMainStation)
        {
            if (sunStatus === "YES")
            {
                if (system.techLevel > 10)
                {
                    this.ups_stationparts = expandDescription("[ups_stationparts]");
                    missionVariables.ups_stationparts = this.ups_stationparts; // missionVariable is needed in choices.
                    this.offering ="UPS_SOLAR1";
                    var message = expandMissionText("ups_sun_offer", {ups_splanetname: this.ups_splanetname, ups_stationparts: this.ups_stationparts});
                    mission.runScreen({screenID: "ups-sun", title: "Solar Transport", message: message, overlay: this._overlay(),
                                        choicesKey: "ups_sun_accepted_yesno"}, this.choiceEvaluation)
                }
            }
            else if (sunStatus === "RESCUEOFFER")
            {
                if (system.government > 1)
                {
                    this.offering = "UPS_SOLAR3";
                    var message = expandMissionText("ups_sun_offer3", {ups_splanetname: this.ups_splanetname});
                    mission.runScreen({screenID: "ups-sun", title: "Solar Resque", message: message, overlay: "solar.png",
                                        choicesKey: "ups_sun_accepted3_yesno"}, this.choiceEvaluation);
                    this.ups_stimerstart = clock.days;
                }
            }
            else if (sunStatus === "EXPLOSION")
            {
                var key = (!this.ups_s2count) ? "ups_sun_unloading3a" : "ups_sun_unloading3";
                var message = expandMissionText(key,
                        {ups_splanetname: this.ups_splanetname, ups_s2count: this.ups_s2count, ups_s2award: this.ups_s2award, ups_s2plural: (this.ups_s2count === 1 ? "" : "s")});
                mission.runScreen({screenID: "ups-sun", title: "Solar Resque", message: message, overlay: this._overlay()});
    			this.ups_sunvisits++;
    			mission.setInstructionsKey(null);
    			this.upsUnmarkSystem(this.ups_splanet, this.name);
    			player.credits += this.ups_s2award;
    			this.sunReset();
                return;
            }
            else if (sunStatus === "RESCUE")
            {
                var daysPast = clock.days - this.ups_stimerstart;
                if (daysPast > 20)
                {
                    var message = expandMissionText("ups_sun_explosion", {ups_days_past: daysPast, ups_splanetname: this.ups_splanetname});
                    mission.runScreen({screenID: "ups-sun", title: "Solar Resque", message: message, overlay: "solar.png"});
                    mission.setInstructionsKey(null);
                    this.upsUnmarkSystem(this.ups_splanet, this.name);
                    this.sunReset();
                }
            }
            return;
        }
        if (player.ship.dockedStation.name === "Sun Research Station Alpha")
        {
            if (sunStatus === "SMALL_DELIVERY")
            {
                this.ups_sun = "NOT_NOW";
                var message = expandMissionText("ups_sun_unloading", {ups_stationparts: this.ups_stationparts});
                mission.runScreen({screenID: "ups-sun", title: "Solar Transport", message: message, overlay: this._overlay()});
                this.ups_sunvisits++;
                player.credits += 250;
                mission.setInstructionsKey(null);
                delete this.ups_stationparts;
                this.upsUnmarkSystem(this.ups_splanet, this.name);
                worldScripts["ups_docs"].increaseReputation(3);
                return;
            }
            if (!this.ups_blackbox && this.researcher_count > 0)
            {
                var message = expandMissionText("ups_sun_offer2",{ups_vessels_lost: this.researcher_count});
                mission.runScreen({screenID: "ups-sun", title: "Solar Retrieval", message: message,
                                    overlay: this._overlay(), choicesKey: "ups_sun_accepted2_yesno"},
                                    this.choiceEvaluation);
                this.offering = "UPS_SOLAR_BLACKBOX";
            }
            if (this.ups_blackbox === "SEARCHING")
            {
                var key;
                if (!this.ups_s2count)
                {
                    key = "ups_sun_unloading2a";
                    delete this.ups_blackbox;
                }
                else
                {
                    key = "ups_sun_unloading2";
                    this.ups_blackbox = "FINISHED";
                }
                var message = expandMissionText(key,{ups_s2count: this.ups_s2count, ups_s2award: this.ups_s2award, ups_s2plural:(this.up_s2count === 1 ? "" : "es")});
                mission.runScreen({screenID: "ups-sun", title: "Solar Retrieval", message: message, overlay: this._overlay()});
                player.credits += this.ups_s2award;
                worldScripts["ups_docs"].increaseReputation(3);
                mission.setInstructionsKey(null);
                delete this.ups_s2count;
                delete this.ups_s2award;
            }
            if (sunStatus === "RESCUE" && !this.ups_rescue)
            {
                this.ups_sun = "EXPLOSION";
                mission.runScreen({screenID: "ups-sun", title: "Solar Resque", messageKey: "ups_sun_rescue", overlay: "solar.png", music: "siren.ogg"});
                mission.setInstructionsKey("ups_sun_small4");
                this.ups_rescue = "YES";
                this.ups_s2count = 10;
                this.ups_s2award = 1500;
                player.ship.dockedStation.energy = player.ship.dockedStation.maxEnergy * 0.15;
            }
        }
    }
    
    this.choiceEvaluation = function (choice)
    {
        switch (this.offering)
        {
            case "UPS_SOLAR1":
            {
                if (choice === "YESSun")
                {
                    this.upsMarkSystem(this.ups_splanet, this.name);
                    mission.setInstructions(expandMissionText("ups_sun_small",
                            {ups_splanetname: this.ups_splanetname, ups_stationparts: this.ups_stationparts}));
                    this.ups_sun = "SMALL_DELIVERY";
                }
                else
                {
                    this.ups_sun = "NOT_NOW";
                    delete this.ups_stationparts;
                    worldScripts["ups_docs"].increaseReputation(-2);
                }
                break;
            }
            case "UPS_SOLAR3":
            {
                if (choice === "YESSun")
                {
                    this.upsMarkSystem(this.ups_splanet, this.name);
                    mission.setInstructions(expandMissionText("ups_sun_small3", {ups_splanetname: this.ups_splanetname}));
                }
                this.ups_sun = "RESCUE"; // mission is always active, even when not choosen by player.
                break;
            }
            case "UPS_SOLAR_BLACKBOX":
            {
                if (choice === "YESSun")
                {
                    this.ups_blackbox = "YES";
                    mission.setInstructions(expandMissionText("ups_sun_small2", {ups_splanetname: this.ups_splanetname}));
                }
                else
                {
                    this.ups_blackbox = "NO";
                }
                break;
            }
            default: log(this.name, "Encountered an illegal mission offer: "+this.offering);
        }
        this.offering = null;
    }
    
    this.setUpShips = function ()
    {
            if (this.ups_splanet === system.ID)
            {
                if (system.countShipsWithRole("ups_dependance") === 0 && !this.ups_rescue)
                {
    					if (this.ups_sun === "RESCUE")
                        {
                            system.addShips("ups_dependance", 1, Vector3D(0, 0, 1.5).fromCoordinateSystem("sps"));
                            this.ups_s2count = 0;
    						this.sunTraders(2, "sw");
                        }
                        else
                        {
                            system.addShips("ups_dependance", 1, Vector3D(0, 0, 2.6).fromCoordinateSystem("sps"));
                            system.addShips("ups_shuttle", 8, Vector3D(0, 0, 2.6).fromCoordinateSystem("sps"));
                            this.researcher_count = 0;
                        }
    					this.sunShips("ups-sun-police", 2, "sp");
    					this.sunShips("pirate", 2, "sw");
    					this.sunShips("pirate", 2, "sp");
    					this.sunTraders(2, "sw");
                        if (worldScripts.transportSchedule)  // has Transports.OXP installed
                        {
                             this.sunShips("fueltransport", 2, "sp");
                        }
    					else
                        {
                            this.sunTraders(2, "sp");
                        }
                }
            }
    }
    
    this.sunShips = function (role, count, coordSystem)
    {
        // also used by "ups_docs"
        for(var i=0;i<count;i++) system.addShipsToRoute(role, 1, Math.random(), coordSystem);
    }
    
    this.sunTraders = function (count, coordSystem)
    {
        for(var i=0; i<count; i++)
        {
            var trader = system.addShipsToRoute("sunskim-trader", 1, Math.random(), coordSystem)[0];
            trader.switchAI("route2sunskimUpsAI.plist");
            trader.primaryRole = "trader";
            if (coordSystem == "sw") trader.AIState = "HEAD_FOR_SUN";
            trader.heatInsulation = (1000 / trader.maxSpeed) + 1;
            if (trader.escorts) for (var j=0; j < trader.escorts.length; j++)
            {
                trader.escorts[j].heatInsulation = trader.heatInsulation;
            }
        }
    }
    
    this.shipExitedWitchspace = function ()
    {
        if (system.isInterstellarSpace) return;
        if (this.ups_sunbase)
        {
            this.setUpShips();
            if (this.ups_blackbox === "FINISHED")
            {
                if (this.ups_sunvisits > 3  && Math.random() < 0.3)
                {
                    delete this.ups_blackbox; // restarts mission
                }
            }
            else
            {
                if (this.ups_blackbox === "SEARCHING") mission.setInstructionsKey(null);
                delete this.ups_blackbox;
                delete this.researcher_count;
            }
            if (this.ups_sun === "NOT_NOW" && Math.random() < 0.1) this.ups_sun = "NO";
            if (this.ups_sun === "NO")
            {
                if (system.techLevel > 10 && Math.random() < 0.2 && this._reputation() > 4)
                {
                    this.ups_sun = "YES";
                }
                else if (system.government > 1 && this.ups_sunvisits > 4 && Math.random() < 0.1)
                {
                    this.ups_sun = "RESCUEOFFER";
                };
            }
        }
    }
    
    this.sunReset = function ()
    {
        mission.setInstructionsKey(null);
        var deletions = ["sun", "sunbase", "sunvisits", "blackbox", "splanet", "stimerstart", "rescue", "splanetname", "s2award", "s2count", "stationparts"];
        for (var i=0; i<deletions.length; i++) delete this["ups_"+deletions[i]];
    }
    
    this._reputation = function ()
    {
        return (0 < oolite.compareVersion("1.77")) ? player.contractReputation : player.parcelReputation;
    }
    
    this._overlay = function() {
        if (player.ship.hudHidden || player.ship.hudAllowsBigGui) {
            return {name:"upsnews-large.png", height:546};
        } else {
            return {name:"upsnews-small.png", height:546};
        }
    }