/*

anarchies.js

Script for Anarchies.oxp.


Oolite
Copyright © 2004-2012 Giles C Williams and contributors

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.


Anarchies.oxp
Copyright © 2007-2012 "Commander McLane"

This work is licensed under the Creative Commons
Attribution-Noncommercial-Share Alike 3.0 Unported License.

To view a copy of this license, visit
http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter
to Creative Commons, 171 Second Street, Suite 300, San Francisco,
California, 94105, USA.

*/

"use strict";

this.name = "Anarchies";
this.description = "Script for Anarchies.oxp";
this.author = "Commander McLane";
this.copyright = "© 2012 Commander McLane";
this.license = "CC-by-nc-sa 3.0";
this.version = "2.9.4";


/* variables */

this.equipment_key = new Array(
	"EQ_ECM",
	"EQ_FUEL_SCOOPS",
	"EQ_ESCAPE_POD",
	"EQ_ENERGY_BOMB",
	"EQ_ENERGY_UNIT",
	"EQ_NAVAL_ENERGY_UNIT",
	"EQ_DOCK_COMP",
	"EQ_GAL_DRIVE",
	"EQ_FUEL_INJECTION",
	"EQ_SCANNER_SHOW_MISSILE_TARGET",
	"EQ_MULTI_TARGET",
	"EQ_ADVANCED_COMPASS",
	"EQ_ADVANCED_NAVIGATIONAL_ARRAY",
	"EQ_TARGET_MEMORY",
	"EQ_SHIELD_BOOSTER",
	"EQ_NAVAL_SHIELD_BOOSTER",
	"EQ_HEAT_SHIELD",
	"EQ_WORMHOLE_SCANNER"
);

// do not translate these names - they are used in ship keys.
this.behemoth_keys = new Array(
	"argus",
	"atlas",
	"bolverk",
	"colossus",
	"geryon",
	"gog",
	"goliath",
	"grid",
	"loki",
	"magog",
	"nimrod",
	"surt",
	"talos",
	"tethys",
	"titan",
	"ymir"
);

this.behemoth_names = expandDescription("[anarchies_behemoth_names]").split("|");

/* functions */

this.$setUpSystem = function (populator) {
	// nothing is done on relaunching from a station
	if (this.systemIsSetUp) return;
	// system is set up
	this.systemIsSetUp = true;
	// if on an amnesty mission, a battle in interstellar space is set up
	if (system.isInterstellarSpace && missionVariables.anarchies_amnesty == "GO_TO_BATTLE" && Math.round((System.infoForSystem(galaxyNumber, this.system1).coordinates.x + System.infoForSystem(galaxyNumber, this.system2).coordinates.x) / 2 * 2.5 * 10) / 10 == player.ship.galaxyCoordinates.x && Math.round((System.infoForSystem(galaxyNumber, this.system1).coordinates.y + System.infoForSystem(galaxyNumber, this.system2).coordinates.y) / 2 * 5 * 10) / 10 == player.ship.galaxyCoordinates.y) {
		system.addShips("behemoth", 3, [0, 0, 0], 15000);
		system.addShips("thargoid", 4, [0, 0, 0], 20000);
		var amnestyReservists = system.addShips("pirate", 5, [0, 0, 0], 15000);
		amnestyReservists.forEach(function (ship) {
			this.$prepareAmnestyReservist(ship);
			ship.switchAI("anarchiesAmnestyReservistAI.plist");
		}, this);
		// more ships join the battle over time
		this.addShipsToBattleTimer = new Timer(this, this.$addShipsToBattle, 15, 15);
		// the player's kills are counted
		this.thargoidsKilled = 0;
		missionVariables.anarchies_amnesty = "FIGHT_IN_BATTLE";
		this.systemIsSetUp = true;
	}
	// nothing more is done in interstellar space
	if (system.isInterstellarSpace) return;
	// in Anarchies the following things are added:
	if (system.government === 0) {
		// low TL Anarchies have the chance to get either a Sentinel or a Renegade Station
		if (system.techLevel < 3) {
			var sdist = 0.2; //where will be the station in the main lane
			var spos = system.mainPlanet.position.multiply(sdist);
			if (system.scrambledPseudoRandomNumber(842) < 0.4) {
				if (!worldScripts.station_validator || worldScripts.station_validator.$deaths("anarchies_sentinel_station").length === 0) {
					if (populator) { //for Oolite 1.79 and above
						system.setPopulator("anarchies_sentinel_station", {
							callback: function (pos) {
								system.addShips("anarchies_sentinel_station", 1, pos, 0);
							}.bind(this),
							location: "COORDINATES",
							coordinates: spos,
							deterministic: true
						});
					} else system.addShipsToRoute("anarchies_sentinel_station", 1, sdist, "wp");
				}
			} else if (system.scrambledPseudoRandomNumber(842) < 0.6) {
				if (!worldScripts.station_validator || worldScripts.station_validator.$deaths("anarchies_renegade_station").length === 0) {
					if (populator) { //for Oolite 1.79 and above
						system.setPopulator("anarchies_renegade_station", {
							callback: function (pos) {
								system.addShips("anarchies_renegade_station", 1, pos, 0);
							}.bind(this),
							location: "COORDINATES",
							coordinates: spos,
							deterministic: true
						});
					} else system.addShipsToRoute("anarchies_renegade_station", 1, sdist, "wp");
				}
			}
		}
		// middle TL Anarchies have the chance to get a Salvage Gang
		else if (system.techLevel < 6) {
			if (system.scrambledPseudoRandomNumber(842) < 0.6) {
				if (!worldScripts.station_validator || worldScripts.station_validator.$deaths("anarchies_salvage_gang").length === 0) {
					var sdist = 0.3; //where will be the station in the main lane
					var spos = system.mainPlanet.position.multiply(sdist);
					if (populator) { //for Oolite 1.79 and above
						system.setPopulator("anarchies_salvage_gang", {
							callback: function (pos) {
								system.addShips("anarchies_salvage_gang", 1, pos, 0);
								system.addShipsToRoute("anarchies_salvager_tugger", 1, 0.25, "wp");
							}.bind(this),
							location: "COORDINATES",
							coordinates: spos,
							deterministic: true
						});
					} else {
						system.addShipsToRoute("anarchies_salvage_gang", 1, sdist, "wp");
						system.addShipsToRoute("anarchies_salvager_tugger", 1, 0.25, "wp");
					}
				}
			}
		}
		// high TL Anarchies have the chance to get a Hacker Outpost, through meeting a henchman
		else {
			if (!worldScripts.station_validator || worldScripts.station_validator.$deaths("anarchies-hacker-outpost").length === 0) {
				var selectPosition = Math.ceil(system.scrambledPseudoRandomNumber(1127) * 4);
				var spos;
				switch (selectPosition) {
					case 1:
						spos = Vector3D(50000, -100000, -200000).fromCoordinateSystem("wpm");
						break;
					case 2:
						spos = Vector3D(100000, 75000, -350000).fromCoordinateSystem("pwm");
						break;
					case 3:
						spos = Vector3D(0, 0.75, 0.5).fromCoordinateSystem("pwu");
						break;
					case 4:
						spos = Vector3D(0, 0.6, 0.5).fromCoordinateSystem("psu");
						break;
				}
				if (populator) {
					system.setPopulator("anarchies_hacker_outpost", {
						callback: function (pos) {
							system.addShips("anarchies_hacker_outpost", 1, pos, 0);
						}.bind(this),
						location: "COORDINATES",
						coordinates: spos,
						deterministic: true
					});
				} else {
					system.addShips("anarchies_hacker_outpost", 1, spos, 0);
				}
				system.addShipsToRoute("anarchies_henchman", 1, 0.2, "wp");
				system.addShipsToRoute("anarchies_henchman", 1, 0.3, "ws");
			}
		}
	}
	// also non-Anarchy systems may get a Salvage Gang
	else if (system.scrambledPseudoRandomNumber(842) < 0.15) {
		if (!worldScripts.station_validator || worldScripts.station_validator.$deaths("anarchies_salvage_gang").length === 0) {
			if (populator) {
				var spos = Vector3D(0.0, 0.0, 0.4).fromCoordinateSystem("wpu");
				system.setPopulator("anarchies_salvage_gang", {
					callback: function (pos) {
						system.addShips("anarchies_salvage_gang", 1, pos, 0);
						system.addShipsToRoute("anarchies_salvager_tugger", 1, 0.35, "wp");
					}.bind(this),
					location: "COORDINATES",
					coordinates: spos,
					deterministic: true
				});
			} else {
				system.addShipsToRoute("anarchies_salvage_gang", 1, 0.4, "wp");
				system.addShipsToRoute("anarchies_salvager_tugger", 1, 0.35, "wp");
			}
		}
	}
	// if an amnesty is offered, a Behemoth waits in-system
	if (missionVariables.anarchies_amnesty == "OFFER_MADE" && system.ID == this.amnestySystem && this.amnestyDeadline - (clock.minutes / 24 / 60) < 2.5 && this.amnestyDeadline - (clock.minutes / 24 / 60) > 0) {
		this.behemothPosition = new Vector3D(system.mainPlanet.position.subtract(system.mainStation.position));
		this.behemothPosition.z = 0;
		this.behemothPosition = this.behemothPosition.direction();
		this.behemothPosition = this.behemothPosition.multiply(system.mainPlanet.radius * 3);
		this.behemothPosition = this.behemothPosition.add(system.mainPlanet.position);
		var behemoth = system.addShips("behemoth-" + this.behemoth_keys[this.behemothNumber], 1, this.behemothPosition, 0);
		behemoth[0].switchAI("anarchiesBehemothAI.plist");
		behemoth[0].primaryRole = "anarchies_behemoth";
		// the Behemoth gets a beacon for easier finding it
		var beaconDistance = 6000;
		if (this.behemoth_keys[this.behemothNumber] == "colossus" || this.behemoth_keys[this.behemothNumber] == "geryon" || this.behemoth_keys[this.behemothNumber] == "gog" || this.behemoth_keys[this.behemothNumber] == "goliath" ||
			this.behemoth_keys[this.behemothNumber] == "loki" || this.behemoth_keys[this.behemothNumber] == "magog" || this.behemoth_keys[this.behemothNumber] == "nimrod" || this.behemoth_keys[this.behemothNumber] == "titan") beaconDistance = -6000;
		var bc = system.addShips("anarchies_behemoth_beacon", 1, behemoth[0].position.add(behemoth[0].orientation.vectorForward().multiply(beaconDistance)), 0)[0];
		bc.beaconLabel = expandDescription("[anarchies_behemoth_beacon]", { name: this.behemoth_names[this.behemothNumber] });
		// up to 20 amnesty seekers are added en route to the Behemoth
		var amnestySeekersNumber = Math.ceil(((1 - (this.amnestyDeadline - (clock.minutes / 24 / 60))) * 20) * ((Math.random() * 0.5) + 0.5));
		for (var i = 0; i < amnestySeekersNumber; i++) {
			var amnestySeeker = system.addShips("pirate", 1, this.behemothPosition.multiply(Math.random()), 10000);
			amnestySeeker[0].savedCoordinates = this.behemothPosition;
			amnestySeeker[0].switchAI("anarchiesAmnestySeekerAI.plist");
			amnestySeeker[0].bounty += Math.round(Math.random() * 50);
		}
		//further amnesty seekers are added by timer
		this.addAmnestySeekersTimer = new Timer(this, this.$addAmnestySeekers, 15, 30);
		// half as much bounty hunters are added en route as well
		for (var i = 0; i < (amnestySeekersNumber / 2); i++) {
			var amnestyHunter = system.addShips("hunter", 1, this.behemothPosition.multiply(Math.random()), 10000);
			amnestyHunter[0].savedCoordinates = this.behemothPosition;
			amnestyHunter[0].switchAI("anarchiesAmnestyHunterAI.plist");
		}
	}
	// after battle, the Behemoth waits in-system again
	if (missionVariables.anarchies_amnesty == "FOUGHT_IN_BATTLE" && system.ID == this.amnestySystem) {
		this.behemothPosition = new Vector3D(system.mainPlanet.position.subtract(system.mainStation.position));
		this.behemothPosition.z = 0;
		this.behemothPosition = this.behemothPosition.direction();
		this.behemothPosition = this.behemothPosition.multiply(system.mainPlanet.radius * 3);
		this.behemothPosition = this.behemothPosition.add(system.mainPlanet.position);
		var behemoth = system.addShips("behemoth-" + this.behemoth_keys[this.behemothNumber], 1, this.behemothPosition, 0);
		behemoth[0].switchAI("anarchiesBehemothAI.plist");
		behemoth[0].primaryRole = "anarchies_behemoth";
		// the Behemoth gets a beacon for easier finding it
		var beaconDistance = 6000;
		if (this.behemoth_keys[this.behemothNumber] == "colossus" || this.behemoth_keys[this.behemothNumber] == "geryon" || this.behemoth_keys[this.behemothNumber] == "gog" || this.behemoth_keys[this.behemothNumber] == "goliath" ||
			this.behemoth_keys[this.behemothNumber] == "loki" || this.behemoth_keys[this.behemothNumber] == "magog" || this.behemoth_keys[this.behemothNumber] == "nimrod" || this.behemoth_keys[this.behemothNumber] == "titan") beaconDistance = -6000;
		var bc = system.addShips("anarchies_behemoth_beacon", 1, behemoth[0].position.add(behemoth[0].orientation.vectorForward().multiply(beaconDistance)), 0)[0];
		bc.beaconLabel = expandDescription("[anarchies_behemoth_beacon]", { name: this.behemoth_names[this.behemothNumber] });
		if (populator === false) system.shipsWithPrimaryRole("buoy-witchpoint")[0].commsMessage(expandDescription("[anarchies_amnesty_seekers]", { name: this.behemoth_names[this.behemothNumber] }));
		// up to 8 returning reservists are added en route to the Behemoth
		var returningReservistsNumber = Math.ceil(Math.random() * 8);
		for (var i = 0; i < returningReservistsNumber; i++) {
			var returningReservist = system.addShips("pirate", 1, this.behemothPosition.multiply(Math.random()), 10000);
			returningReservist[0].savedCoordinates = this.behemothPosition;
			this.$prepareAmnestyReservist(returningReservist[0]);
			returningReservist[0].switchAI("anarchiesReturningReservistAI.plist");
		}
		//further returning reservists are added by timer
		this.addReturningReservistsTimer = new Timer(this, this.$addReturningReservists, 15, 30);
	}
}

this.$addAmnestySeekers = function $addAmnestySeekers() {
	// adds more ships heading for the waiting Behemoth
	var amnestySeeker = system.addShipsToRoute("pirate", 1, 0);
	amnestySeeker[0].savedCoordinates = this.behemothPosition;
	amnestySeeker[0].switchAI("anarchiesAmnestySeekerAI.plist");
	amnestySeeker[0].bounty += Math.round(Math.random() * 50);
	this.addAmnestySeekersTimer.interval = (Math.random() * 150) + 20;
}

this.$addShipsToBattle = function $addShipsToBattle() {
	// randomly adds Thargoids and/or amnesty seeking pirates to the interstellar battle
	// if only 4 or less Thargoids are left, no further reinforcements are added
	if (system.countShipsWithRole("thargoid") < 5) {
		this.addShipsToBattleTimer.stop();
		return;
	}
	if (Math.random() < 0.65) system.addShips("thargoid", 1);
	if (Math.random() < 0.65) {
		var amnestyReservist = system.addShips("pirate", 1);
		this.$prepareAmnestyReservist(amnestyReservist[0]);
		amnestyReservist[0].switchAI("anarchiesAmnestyReservistAI.plist");
	}
	this.addShipsToBattleTimer.interval = (Math.random() * 30) + 10;
}

this.$addReturningReservists = function $addReturningReservists() {
	// adds more ships heading for the waiting Behemoth
	var returningReservist = system.addShipsToRoute("pirate", 1, 0);
	returningReservist[0].savedCoordinates = this.behemothPosition;
	this.$prepareAmnestyReservist(returningReservist[0]);
	returningReservist[0].switchAI("anarchiesReturningReservistAI.plist");
	this.addReturningReservistsTimer.interval = (Math.random() * 150) + 20;
}

this.$addBountyHunters = function () {
	// fugitive players may encounter bounty hunters, when they enter a new system or launch
	if (player.bounty > 50 && Math.random() < 0.35) {
		system.addShips("anarchies_bounty_hunter", 2, player.ship.position, 15000);
		system.addShipsToRoute("anarchies_bounty_hunter", 2, 0.5, "wp");
	}
	// very fugitive players WILL encounter bounty hunters close to the main station
	if (player.bounty > 255) system.addShipsToRoute("anarchies_bounty_hunter", 2, 1.0);
}

this.$bonusOnBounty = function (target) {
	// if the player isn't clean and his target was a criminal, he may get a bounty reduction instead of a payment
	if (player.bounty > 0 && !target.hasRole("asteroid") && target.bounty > 0 && Math.random() < 0.16) {
		// the reduction depends on the target's bounty
		player.bounty -= Math.ceil(Math.sqrt(target.bounty) * 2 / 3);
		// the player's bounty isn't reduced below 0
		if (player.bounty < 0) player.bounty = 0;
		// the awarded bounty is reduced from the player's money again. unfortunately this isn't yet displayed in the default kill-message
		player.credits -= target.bounty;
		// another message is displayed
		player.consoleMessage(expandDescription("[anarchies_help_thanks]"), 6);
	}
}

this.$setUpEquipmentFailureTimer = function () {
	if (!this.equipmentFailureTimer) {
		this.equipmentFailureTimer = new Timer(this, this.$salvagedEquipmentFailed, 20, 20);
	}
	this.equipmentFailureTimer.stop();
}

this.$salvagedEquipmentFailed = function $salvagedEquipmentFailed() {
	// gives a chance that salvaged equipment bought from a Salvage Gang may fail
	// or become working as first hand equipment by simply removing the failure chance
	// not executed if no salvaged equipment has been installed
	if (!missionVariables.anarchies_salvaged_equipment || missionVariables.anarchies_salvaged_equipment == 0) return;
	// there is a 1% random chance for failing, and 0.5% for becoming indistinguishable from first hand equipment
	var failureChance = Math.random();
	if (failureChance < 0.73 || failureChance >= 0.745) return;
	// one of the possible equipment items is chosen
	var equipCount = 0;
	var failingEquipment;
	// the number of possible equipment items is established and stored in equipCount
	for (var i = 0; i < this.equipment_key.length; i++) {
		if ((missionVariables.anarchies_salvaged_equipment & Math.pow(2, i)) != 0) equipCount++;
	}
	// one of those is chosen with equal probability
	for (var i = 0; i < this.equipment_key.length; i++) {
		if ((missionVariables.anarchies_salvaged_equipment & Math.pow(2, i)) != 0) {
			if (Math.random() < (1 / equipCount)) {
				failingEquipment = i;
				break;
			}
			equipCount--;
		}
	}
	// check if the equipment hasn't been disabled otherwise (in which case it is also removed from the list, because it has to be repaired or newly bought anyway)
	// also check whether we want to make it fail, or make it like first hand equipment; it fails only on the 1% chance from above
	if (player.ship.equipmentStatus(this.equipment_key[failingEquipment]) === "EQUIPMENT_OK" && failureChance < 0.74) {
		// equipment status is set to "damaged" and player is notified
		player.ship.setEquipmentStatus(this.equipment_key[failingEquipment], "EQUIPMENT_DAMAGED");
		player.consoleMessage(expandDescription("[anarchies_equipment_broke]", { equipment: EquipmentInfo.infoForKey(this.equipment_key[failingEquipment]).name }), 6);
	}
	// item is removed from the list of installed salvaged equipment
	// which also happens on the 0.5% chance from above, or if the equipment was damaged or removed otherwise
	missionVariables.anarchies_salvaged_equipment = missionVariables.anarchies_salvaged_equipment & (Math.pow(2, this.equipment_key.length) - 1 - Math.pow(2, failingEquipment));
}

this.$newBountyCalculation1 = function (cause) {
	// stores the player's bounty before jumping
	this.playerBounty = player.bounty;
	// a galactic jump still clears the player's bounty
	if (cause == "galactic jump") this.playerBounty = 0;
}

this.$newBountyCalculation2 = function () {
	// calculates the bounty on the player after arriving in a new system, replacing Oolite's own formula which halves the bounty
	// does nothing for a misjump
	if (system.isInterstellarSpace) return;
	// the default mechanism is used for bounties < 51
	// between 51 and 60 there is a small chance only of reduction, making it likely to stay fugitive for a long time
	if (this.playerBounty > 50 && this.playerBounty < 61 && Math.random() > 0.93) player.bounty = 50;
	// above 60 there is a reduction by 10% (rounded)
	if (this.playerBounty > 60) player.bounty = Math.ceil(this.playerBounty * 0.9);
}

this.$bribeGalcopOfficial = function () {
	// only started if no other missionscreen is running and only once before next jump
	if (!missionVariables.offering && !this.bribeOffer) {
		// offer is made, first missionscreen
		mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_bribeoffer", choicesKey: "Anarchies_bribeoffer_offer" }, this.$evaluateBribeofferChoices);
		this.bribeOffer = "APPROACH";
		// mission_offering is set, to prevent legacy scripts from running missionscreens
		missionVariables.offering = "ANARCHIES";
		// dice is thrown to establish the bribesum requested
		var bribedice = Math.random();
		if (bribedice < 0.3) {
			this.bribesum = 8000;
			this.lowersum = 5000;
			missionVariables.anarchies_bribestring = "8000";
			missionVariables.anarchies_lowerstring = "5000";
		}
		else if (bribedice < 0.7) {
			this.bribesum = 10000;
			this.lowersum = 6000;
			missionVariables.anarchies_bribestring = "10000";
			missionVariables.anarchies_lowerstring = "6000";
		}
		else {
			this.bribesum = 12000;
			this.lowersum = 7000;
			missionVariables.anarchies_bribestring = "12000";
			missionVariables.anarchies_lowerstring = "7000";
		}
	}
	else if (this.bribeOffer == "DONE") {
		// if no further offers are made, let's get rid of some variables which are of no need anymore
		delete this.bribesum;
		delete this.lowersum;
		delete missionVariables.anarchies_bribestring;
		delete missionVariables.anarchies_lowerstring;
		this.bribeOffer = "END";
		// is reset only by the next jump
		// legacy scripts may run missionscreens again
		delete missionVariables.offering;
	}
}

this.$evaluateBribeofferChoices = function (choice) {
	if (this.bribeOffer === "APPROACH") {
		// second screen, depending on YES or NO
		if (choice === "Anarchies_offer_2_NO") {
			mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_bribeoffer_rejected" });
			this.bribeOffer = "DONE";
			// end, no other offer is made before leaving the system
		}
		else if (choice === "Anarchies_offer_1_YES") {
			mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_bribechoice", choicesKey: "Anarchies_bribe_choices" }, this.$evaluateBribeofferChoices);
			this.bribeOffer = "OFFER";
		}
	}
	else if (this.bribeOffer === "OFFER") {
		// third screen, you may have accepted, refused, or tried to bargain
		if (choice === "Anarchies_choice_1_YES") {
			// accepted, deal made
			player.credits -= this.bribesum;
			mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_bribed" });
			player.bounty = 0;
			this.bribeOffer = "DONE";
		}
		else if (choice === "Anarchies_choice_2_NO") {
			// refused, bad choice!
			mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_not_bribed" });
			player.bounty = 350;
			this.bribeOffer = "DONE";
			// no further offer before leaving the system, but beware of bounty hunters!
		}
		else if (choice === "Anarchies_choice_3_LOWER") {
			// tried to bargain, let's see...
			if (Math.random() < 0.6) {
				// successful, you got your bargain
				player.credits -= this.lowersum;
				mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_lowered" });
				player.bounty = 0;
			}
			else {
				// oh, oh; bad karma
				mission.runScreen({ screenID: "anarchies_bribe", titleKey: "Anarchies_bribetitle", messageKey: "Anarchies_not_lowered" });
				player.bounty = 350;
			}
			this.bribeOffer = "DONE";
			// no further offer before leaving the system, see above
		}
	}
}

this.$multipass = function () {
	// determines the result of using a Multipass, depending on the player's bounty and credits
	// the following will be passed to a shared missionscreen display function
	this.specialEquipment = "EQ_MULTIPASS";
	this.pageTitle = expandDescription("[anarchies_connecting]");
	this.dotCount = 0;
	this.countEnd = 12;
	// costs of sub-ether connection are calculated according to the player's current bounty
	if (player.bounty > 120) {
		// have you got enough money?
		if (player.credits >= 7000) {
			this.notification = "Anarchies_Multipass_used_successfully";
			player.bounty = 0;
			player.credits -= 7000;
		}
		// or not?
		else {
			this.notification = "Anarchies_Multipass_connection_cancelled";
			player.bounty = 100;
			player.credits = 0;
			this.countEnd = 6;
		}
	}
	else if (player.bounty > 50) {
		if (player.credits >= 5000) {
			this.notification = "Anarchies_Multipass_used_successfully";
			player.bounty = 0;
			player.credits -= 5000;
		}
		else {
			this.notification = "Anarchies_Multipass_connection_cancelled";
			player.bounty = 45;
			player.credits = 0;
			this.countEnd = 6;
		}
	}
	else if (player.bounty > 20) {
		if (player.credits >= 3000) {
			this.notification = "Anarchies_Multipass_used_successfully";
			player.bounty = 0;
			player.credits -= 3000;
		}
		else {
			this.notification = "Anarchies_Multipass_connection_cancelled";
			player.bounty = 18;
			player.credits = 0;
			this.countEnd = 6;
		}
	}
	else {
		if (player.credits >= 1000) {
			this.notification = "Anarchies_Multipass_used_successfully";
			player.bounty = 0;
			player.credits -= 1000;
		}
		else {
			this.notification = "Anarchies_Multipass_connection_cancelled";
			player.bounty = 1;
			player.credits = 0;
			this.countEnd = 6;
		}
	}
	// progress notification is started with a timer, relevant data has been stored above
	this.dotTimer = new Timer(this, this.$progressDots, 0, 1.2);
}

this.$multipassedID = function () {
	// calculates the result of buying a Multi-passed ID, depending on a dice-roll
	// again data for the missionscreen display function, which is the same as above
	this.specialEquipment = "EQ_MULTIPASSED_ID";
	this.pageTitle = expandDescription("[anarchies_install_transponder]");
	this.dotCount = 0;
	this.countEnd = 6;
	// whose second-hand transponder you get, is purely random
	var chance = Math.random();
	if (chance < 0.15) {
		this.notification = "Anarchies_Multipassed_ID_highFUG";
		player.bounty = 205;
	}
	else if (chance < 0.3) {
		this.notification = "Anarchies_Multipassed_ID_lowFUG";
		player.bounty = 58;
	}
	else if (chance < 0.6) {
		this.notification = "Anarchies_Multipassed_ID_OFFEND";
		player.bounty = 15;
	}
	else {
		this.notification = "Anarchies_Multipassed_ID_CLEAN";
		player.bounty = 0;
	}
	// the rest is again done with a timer, to which the relevant data is passed
	this.dotTimer = new Timer(this, this.$progressDots, 0, 1.2);
}

this.$progressDots = function $progressDots() {
	// prints a couple of dots to indicate a process being executed, afterwards gives out a result screen
	// used by both this.$multipass and this.$multipassedID
	if (this.dotCount > this.countEnd) {
		// when we had enough small dots plotted, the final result is displayed, multipass or multipassed ID removed, and mission_offering cleared
		this.dotTimer.stop();
		delete this.dotTimer;
		mission.runScreen({ screenID: "anarchies_progress", title: this.pageTitle, messageKey: this.notification });
		player.ship.removeEquipment(this.specialEquipment);
		return;
	}
	mission.runScreen({ screenID: "anarchies_progress", title: this.pageTitle, messageKey: "Anarchies_progress_notification_dot_" + this.dotCount, choicesKey: "Anarchies_empty" });
	this.dotCount++;
}

this.$rippedOffByRenegades = function () {
	// nothing happens if the player is fugitive
	if (player.bounty > 50) return;
	if (!this.rippedOff) {
		mission.runScreen({ screenID: "anarchies_renegades", title: "Ambush", messageKey: "Anarchies_Renegade_Station_Rippedoff" }, this.$launchPlayer);
		// how much money have you got?
		if (player.credits < 10000) {
			mission.addMessageTextKey("Anarchies_Renegade_Station_Rippedoff_1");
			player.credits = 0;
		}
		else {
			mission.addMessageTextKey("Anarchies_Renegade_Station_Rippedoff_2");
			player.credits -= 10000;
		}
		this.rippedOff = true;
	}
}

this.$launchPlayer = function () {
	player.ship.removeAllCargo();
	this.rippedOff = null;
	player.ship.launch();
	// and off you go
}

this.$setSalvagedEquipmentChances = function () {
	// sets the availability chances for salvaged equipment items
	// these are verified in equipment.plist
	missionVariables.salvaged_ecm_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_fuel_scoops_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_escape_pod_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_energy_bomb_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_energy_unit_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_naval_unit_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_dock_comp_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_gal_drive_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_injectors_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_target_enhancement_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_multi_target_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_adv_compass_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_adv_array_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_target_memory_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_shield_booster_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_naval_booster_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_heat_shield_chance = Math.floor(Math.random() * 100);
	missionVariables.salvaged_wormhole_scanner_chance = Math.floor(Math.random() * 100);
}

this.$salvagedEquipmentBought = function (boughtEquipmentKey) {
	// checks which salvaged equipment was bought at a Salvage Gang
	for (var i = 0; i < this.equipment_key.length; i++) {
		// whatever it was ...
		if (boughtEquipmentKey == (this.equipment_key[i] + "_SALVAGED")) break;
	}
	// the equipment is stored in a missionVariable, to make it failable later
	missionVariables.anarchies_salvaged_equipment = missionVariables.anarchies_salvaged_equipment | Math.pow(2, i);
	// to make it usable the salvaged equipment is replaced with the standard variant
	player.ship.removeEquipment(boughtEquipmentKey);
	player.ship.awardEquipment(this.equipment_key[i]);
}

this.$resetSalvagedEquipmentChances = function () {
	// delete a bunch of unnecessary missionVariables at launching from a Salvage Gang
	delete missionVariables.salvaged_ecm_chance;
	delete missionVariables.salvaged_fuel_scoops_chance;
	delete missionVariables.salvaged_escape_pod_chance;
	delete missionVariables.salvaged_energy_bomb_chance;
	delete missionVariables.salvaged_energy_unit_chance;
	delete missionVariables.salvaged_naval_unit_chance;
	delete missionVariables.salvaged_dock_comp_chance;
	delete missionVariables.salvaged_gal_drive_chance;
	delete missionVariables.salvaged_injectors_chance;
	delete missionVariables.salvaged_target_enhancement_chance;
	delete missionVariables.salvaged_multi_target_chance;
	delete missionVariables.salvaged_adv_compass_chance;
	delete missionVariables.salvaged_adv_array_chance;
	delete missionVariables.salvaged_target_memory_chance;
	delete missionVariables.salvaged_shield_booster_chance;
	delete missionVariables.salvaged_naval_booster_chance;
	delete missionVariables.salvaged_heat_shield_chance;
	delete missionVariables.salvaged_wormhole_scanner_chance;
}

this.$displayTimeDHM = function (minutes) {
	// this function takes an integer number of minutes and converts it into a string of the form
	// 'd' days, 'h' hours and 'm' minutes, accomodating all cases of singular, plural, and 0
	if (minutes < 0) minutes = -minutes;
	var daysComponent = Math.floor(minutes / 24 / 60);
	var hoursComponent = Math.floor((minutes % (24 * 60)) / 60);
	var minutesComponent = minutes % 60;
	var timeString = "";
	if (daysComponent == 1) timeString += daysComponent + expandDescription("[anarchies_day_single]");
	if (daysComponent > 1) timeString += daysComponent + expandDescription("[anarchies_day_plural]");
	if (timeString != "") {
		if (hoursComponent > 0 && minutesComponent > 0) timeString += ", ";
		else if (hoursComponent > 0) timeString += expandDescription("[anarchies_and]");
	}
	if (hoursComponent == 1) timeString += hoursComponent + expandDescription("[anarchies_hour_single]");
	if (hoursComponent > 1) timeString += hoursComponent + expandDescription("[anarchies_hour_plural]");
	if (timeString != "" && minutesComponent > 0) timeString += expandDescription("[anarchies_and]");
	if (minutesComponent == 1) timeString += minutesComponent + expandDescription("[anarchies_minute_single]");
	if (minutesComponent > 1) timeString += minutesComponent + expandDescription("[anarchies_minute_plural]");
	if (timeString == "") timeString = expandDescription("[anarchies_notime]");
	return timeString;
}

this.$amnestyOffer = function () {
	if (!missionVariables.anarchies_amnesty && worldScripts.behemoth && (((player.ship.dockedStation.hasRole("free_trade_zone") || player.ship.dockedStation.hasRole("anarchies_hacker_outpost") || player.ship.dockedStation.hasRole("anarchies_salvage_gang")) && Math.random() < 0.2) || (system.government == 0 && Math.random() < 0.05) || (system.government > 0 && Math.random() < 0.02))) {
		// a system in the galaxy is randomly chosen
		this.amnestySystem = Math.floor(Math.random() * 256);
		if (this.amnestySystem == system.ID) return;
		// and the route from the current system calculated
		var amnestyRoute = system.info.routeToSystem(System.infoForSystem(galaxyNumber, this.amnestySystem), "OPTIMIZED_BY_TIME");
		// if the chosen system is unreachable, the code stops
		if (amnestyRoute == null) return;
		// the amnesty system, its name and distance are stored in the save-file, both for later reference and for transfering them to a mission screen
		missionVariables.anarchies_amnesty_system = this.amnestySystem;
		missionVariables.anarchies_amnesty_system_name = System.infoForSystem(galaxyNumber, this.amnestySystem).name;
		var amnestySystemDistance = Math.round(system.info.distanceToSystem(System.infoForSystem(galaxyNumber, this.amnestySystem)) * 5) / 5;
		missionVariables.anarchies_amnesty_system_distance = amnestySystemDistance;
		// the deadline is calculated:
		// first the minimum time is extended by a factor of 1.25, (it's possible to play with that; for bigger distances it's impossible to meet the deadline with few long jumps)
		// then it is rounded to noon of the day
		this.amnestyDeadline = Math.round((clock.hours + (amnestyRoute.time * 1.25) + 12) / 24) - 0.5;
		// if you could only arrive earliest in the afternoon, the deadline is extended one day
		if (this.amnestyDeadline < (clock.hours + amnestyRoute.time) / 24) this.amnestyDeadline += 1;
		// the deadline is stored in the save-file for later reference
		missionVariables.anarchies_amnesty_deadline = this.amnestyDeadline;
		// the day is stored in the save-file, again for displaying it on mission screens
		missionVariables.anarchies_amnesty_offer_date = Math.floor(this.amnestyDeadline);
		// like for contracts, the start window is calculated, and saved in the save-file for display on screen
		// first the time until deadline
		missionVariables.anarchies_amnesty_time = this.$displayTimeDHM((this.amnestyDeadline * 24 * 60) - clock.minutes);
		// then the end of the start window (time until deadline minus minimum travel time)
		missionVariables.anarchies_amnesty_start_time = this.$displayTimeDHM((this.amnestyDeadline * 24 * 60) - clock.minutes - Math.floor(amnestyRoute.time * 60));
		// one of the Behemoths is chosen randomly
		this.behemothNumber = Math.floor(Math.random() * 16);
		this.behemothName = this.behemoth_names[this.behemothNumber];
		missionVariables.anarchies_amnesty_behemoth_name = this.behemothName;
		missionVariables.anarchies_amnesty_behemoth_number = this.behemothNumber;
		// all necessary information is put on a news screen
		mission.runScreen({ screenID: "anarchies_amnesty", titleKey: "Anarchies_iNewstitle", messageKey: "Anarchies_amnesty_news", overlay: "anarchies_iNews.png" });
		missionVariables.anarchies_amnesty = "OFFER_MADE";
		return;
	}

	if (missionVariables.anarchies_amnesty == "OFFER_MADE" && system.ID == this.amnestySystem && player.ship.dockedStation.hasRole("behemoth-" + this.behemoth_keys[this.behemothNumber])) {
		// no further ships jump into the system
		this.addAmnestySeekersTimer.stop();
		// one of the systems in range is randomly chosen
		this.system1 = system.info.systemsInRange()[Math.floor(Math.random() * system.info.systemsInRange().length)];
		// a second one is chosen, in range of the first system
		this.system2 = System.infoForSystem(galaxyNumber, this.system1.systemID).systemsInRange()[Math.floor(Math.random() * System.infoForSystem(galaxyNumber, this.system1.systemID).systemsInRange().length)];
		// the two systems are ordered in proximity to the current system
		if (system.info.distanceToSystem(System.infoForSystem(galaxyNumber, this.system1.systemID)) > system.info.distanceToSystem(System.infoForSystem(galaxyNumber, this.system2.systemID))) {
			var changeSystems = this.system1;
			this.system1 = this.system2;
			this.system2 = changeSystems;
		}
		// their numbers and names are put into mission variables, for later reference and to put them on a mission screen
		missionVariables.anarchies_amnesty_destination1_name = this.system1.name;
		missionVariables.anarchies_amnesty_destination2_name = this.system2.name;
		this.system1 = this.system1.systemID;
		this.system2 = this.system2.systemID;
		missionVariables.anarchies_amnesty_destination1 = this.system1;
		missionVariables.anarchies_amnesty_destination2 = this.system2;
		// the mission briefing is given
		mission.runScreen({ screenID: "anarchies_amnesty", titleKey: "Anarchies_amnesty_offer", messageKey: "Anarchies_amnesty_briefing", overlay: "anarchies_military_star.png" });
		// the two systems are marked, the player has to misjump between them
		mission.markSystem(this.system1, this.system2);
		// the relevant information is put on the manifest screen
		mission.setInstructionsKey("Anarchies_amnesty_order");
		// other ships join the player, he may travel with them
		this.launchAmnestyReservistsTimer = new Timer(this, this.$launchAmnestyReservist, 5, 20);
		missionVariables.anarchies_amnesty = "GO_TO_BATTLE";
		return;
	}
	if (missionVariables.anarchies_amnesty == "FOUGHT_IN_BATTLE" && system.ID == this.amnestySystem && player.ship.dockedStation.hasRole("behemoth-" + this.behemoth_keys[this.behemothNumber])) {
		// no further ships jump into the system
		this.addReturningReservistsTimer.stop();
		// how well did the player?
		if (missionVariables.anarchies_amnesty_thargoids_killed >= 4) {
			// he succeeded, amnesty is granted
			missionVariables.anarchies_amnesty_order_date = this.amnestyDeadline - 14.5;
			mission.runScreen({ screenID: "anarchies_amnesty", titleKey: "Anarchies_amnesty_granted", messageKey: "Anarchies_amnesty_debriefing_yes", overlay: "anarchies_military_star.png" });
			player.bounty = 0;
		}
		else {
			// he didn't succeed, but there are different levels of failure
			switch (missionVariables.anarchies_amnesty_thargoids_killed) {
				// complete failure
				case 0:
					missionVariables.anarchies_amnesty_debriefing_1 = expandDescription("[anarchies_no_kills]");
					missionVariables.anarchies_amnesty_debriefing_2 = "";
					break;
				// at least he did something
				case 1:
					missionVariables.anarchies_amnesty_debriefing_1 = expandDescription("[anarchies_one_kill]");
					missionVariables.anarchies_amnesty_debriefing_2 = expandDescription("[anarchies_reward]", { amount: 500 });
					player.credits += 500;
					break;
				// almost there, but not yet
				default:
					missionVariables.anarchies_amnesty_debriefing_1 = expandDescription("[anarchies_some_kills]");
					missionVariables.anarchies_amnesty_debriefing_2 = expandDescription("[anarchies_reward]", { amount: 1000 });
					player.credits += 1000;
			}
			// information about the outcome
			mission.runScreen({ screenID: "anarchies_amnesty", titleKey: "Anarchies_amnesty_denied", messageKey: "Anarchies_amnesty_debriefing_no", overlay: "anarchies_military_star.png" });
			player.credits += 1000;
		}
		// mission ends, clear all variables
		this.$endAmnestyOffer();
		// launch some happy, freshly pardoned pirates
		var clearedReservists = Math.ceil(Math.random() * 6);
		while (clearedReservists > 0) {
			var clearedReservist = system.shipsWithPrimaryRole("anarchies_behemoth")[0].launchShipWithRole("pirate");
			clearedReservist.bounty = 0;
			clearedReservist.setAI("exitingTraderAI.plist");
			clearedReservists--;
		}
	}
}

this.$displayTimeTillAmnestyDeadline = function () {
	// displays the remaining time till deadline, and the remaining distance on the manifest screen
	// if there isn't currently an offer, nothing is displayed
	if (!missionVariables.anarchies_amnesty || missionVariables.anarchies_amnesty != "OFFER_MADE") return;
	// if the offer has expired, the information is cleared
	if ((this.amnestyDeadline * 24 * 60) - clock.minutes < 0) {
		mission.setInstructions(null);
		return;
	}
	// the remaining time is recalculated and the mission variable refreshed
	missionVariables.anarchies_amnesty_time = this.$displayTimeDHM((this.amnestyDeadline * 24 * 60) - clock.minutes);
	// the remaining distance is recalculated and the mission variable refreshed
	var amnestySystemDistance = Math.round(system.info.distanceToSystem(System.infoForSystem(galaxyNumber, this.amnestySystem)) * 5) / 5;
	missionVariables.anarchies_amnesty_system_distance = amnestySystemDistance;
	mission.setInstructionsKey("Anarchies_amnesty_reminder");
}

this.$launchAmnestyReservist = function $launchAmnestyReservist() {
	// launch a candidate for amnesty from the Behemoth
	var amnestyReservist = system.shipsWithPrimaryRole("anarchies_behemoth")[0].launchShipWithRole("pirate");
	this.$prepareAmnestyReservist(amnestyReservist);
	amnestyReservist.switchAI("anarchiesAmnestyReservistAI.plist");
	this.launchAmnestyReservistsTimer.interval = (Math.random() * 40) + 5;
}

this.$prepareAmnestyReservist = function (ship) {
	// the reservists need some amendments to their ship scripts
	// jumping towards the battle
	ship.script.$jumpToTargetSystem = function () {
		if (system.ID == worldScripts.Anarchies.system1) {
			var targetSystem = worldScripts.Anarchies.system2;
			this.ship.scriptedMisjump = true;
		}
		else {
			var targetSystem = worldScripts.Anarchies.system1;
		}
		if (this.ship.exitSystem(targetSystem) == false) this.ship.reactToAIMessage("WITCHSPACE_UNAVAILABLE");
	}
	// check for correct system after the battle
	ship.script.$checkSystem = function () {
		if (system.ID == worldScripts.Anarchies.amnestySystem) {
			this.ship.savedCoordinates = worldScripts.Anarchies.behemothPosition;
			this.ship.reactToAIMessage("AMNESTY_SYSTEM");
		}
		else {
			this.ship.reactToAIMessage("SYSTEM1");
		}
	}
	// jumping towards the Behemoth after battle
	ship.script.$jumpToAmnestySystem = function () {
		if (system.info.distanceToSystem(System.infoForSystem(galaxyNumber, worldScripts.Anarchies.amnestySystem)) < 7) {
			var targetSystem = worldScripts.Anarchies.amnestySystem;
		}
		else {
			var targetSystem = worldScripts.Anarchies.system1;
		}
		if (this.ship.exitSystem(targetSystem) == false) this.ship.reactToAIMessage("WITCHSPACE_UNAVAILABLE");
	}
	// new bounty calculation, analog to the player
	// storing the bounty before jump
	ship.script.$CMcLoldShipWillEnterWormhole = ship.script.shipWillEnterWormhole;
	ship.script.shipWillEnterWormhole = function () {
		this.shipBounty = this.ship.bounty;
		if (ship.script.$CMcLoldShipWillEnterWormhole) ship.script.$CMcLoldShipWillEnterWormhole();
	}
	// restoring the bounty according to a new calculation after jump
	ship.script.$CMcLoldShipExitedWormhole = ship.script.shipExitedWormhole;
	ship.script.shipExitedWormhole = function () {
		if (this.ship.script.shipBounty) {
			if (this.shipBounty > 50) this.ship.bounty = this.shipBounty;
			if (this.shipBounty > 55) this.ship.bounty = Math.ceil(this.shipBounty * 0.9);
			if (system.isInterstellarSpace) this.ship.bounty = this.shipBounty;
		}
		if (ship.script.$CMcLoldShipExitedWormhole) ship.script.$CMcLoldShipExitedWormhole();
	}
	// when created first time, the ship has a considerable bounty
	ship.bounty += Math.round(Math.random() * 50);
	// to enable sunskimming, it gets proper insulation
	ship.heatInsulation = 7;
}

this.$endAmnestyOffer = function () {
	// clear all traces of the amnesty mission
	if (this.system1) mission.unmarkSystem(this.system1, this.system2);
	mission.setInstructions(null);
	delete missionVariables.anarchies_amnesty_system;
	delete missionVariables.anarchies_amnesty_system_name;
	delete missionVariables.anarchies_amnesty_system_distance;
	delete missionVariables.anarchies_amnesty_deadline;
	delete missionVariables.anarchies_amnesty_offer_date;
	delete missionVariables.anarchies_amnesty_time;
	delete missionVariables.anarchies_amnesty_start_time;
	delete missionVariables.anarchies_amnesty_behemoth_name;
	delete missionVariables.anarchies_amnesty_destination1;
	delete missionVariables.anarchies_amnesty_destination2;
	delete missionVariables.anarchies_amnesty_destination1_name;
	delete missionVariables.anarchies_amnesty_destination2_name;
	delete missionVariables.anarchies_amnesty_thargoids_killed;
	delete missionVariables.anarchies_amnesty_order_date;
	delete missionVariables.anarchies_amnesty_debriefing_1;
	delete missionVariables.anarchies_amnesty_debriefing_2;
	delete missionVariables.anarchies_amnesty;
}


/* event handlers */

this.startUp = function () {
	this.$setUpEquipmentFailureTimer();
	this.$firstMissionScreenOpportunity = true;
	this.escapePodUsed = false;
	this.systemIsSetUp = false;
	// restore some amnesty-related variables from the save-game
	if (missionVariables.anarchies_amnesty) {
		this.amnestySystem = missionVariables.anarchies_amnesty_system;
		this.amnestyDeadline = missionVariables.anarchies_amnesty_deadline;
		this.behemothName = missionVariables.anarchies_amnesty_behemoth_name;
		this.behemothNumber = missionVaraibles.anarchies_amnesty_behemoth_number;
		this.system1 = missionVariables.anarchies_amnesty_destination1;
		this.system2 = missionVariables.anarchies_amnesty_destination2;
	}
}

this.shipWillEnterWitchspace = function (cause) {
	this.$newBountyCalculation1(cause);
	this.systemIsSetUp = false;
	// leaving the battle means leaving the battle
	if (missionVariables.anarchies_amnesty == "FIGHT_IN_BATTLE") missionVariables.anarchies_amnesty = "FOUGHT_IN_BATTLE";
	// all timers are stopped if the player leaves the system
	if (this.addAmnestySeekersTimer && this.addAmnestySeekersTimer.isRunning) this.addAmnestySeekersTimer.stop();
	if (this.launchAmnestyReservistsTimer && this.launchAmnestyReservistsTimer.isRunning) this.launchAmnestyReservistsTimer.stop();
	if (this.addShipsToBattleTimer && this.addShipsToBattleTimer.isRunning) this.addShipsToBattleTimer.stop();
	if (this.addReturningReservistsTimer && this.addReturningReservistsTimer.isRunning) this.addReturningReservistsTimer.stop();
}

this.shipWillExitWitchspace = function () {
	this.$newBountyCalculation2();
	this.$addBountyHunters();
	// four days after the last amnesty deadline the variables are cleared, and another offer becomes possible
	if (missionVariables.anarchies_amnesty && missionVariables.anarchies_amnesty != "FIGHT_IN_BATTLE" && clock.days > this.amnestyDeadline + 4) this.$endAmnestyOffer();
	this.$setUpSystem(false);
	delete this.bribeOffer;
}

this.shipExitedWitchspace = function () {
	if (missionVariables.anarchies_amnesty == "OFFER_MADE" && system.ID == this.amnestySystem && this.amnestyDeadline - (clock.minutes / 24 / 60) < 2.5 && this.amnestyDeadline - (clock.minutes / 24 / 60) > 0) {
		var buoy = system.shipsWithPrimaryRole("buoy-witchpoint");
		if (buoy && buoy.length > 0) buoy[0].commsMessage(expandDescription("[anarchies_amnesty_seekers]", { name: this.behemoth_names[missionVariables.anarchies_amnesty_behemoth_number] }));
	}
}

this.shipWillDockWithStation = function (station) {
	this.$firstMissionScreenOpportunity = true;

	// stops the equipment failure timer while docked
	this.equipmentFailureTimer.stop();
	// restores the player's bounty after using the escape pod
	if (this.escapePodUsed) {
		player.bounty = this.playerBounty;
		this.escapePodUsed = false;
	}
	if (station.hasRole("anarchies_salvage_gang")) this.$setSalvagedEquipmentChances();
}

this.missionScreenOpportunity = function () {
	if (this.$firstMissionScreenOpportunity) {
		this.$firstMissionScreenOpportunity = false;
		if (player.ship.docked && player.ship.dockedStation.isMainStation && this.bribeOffer != null && this.bribeOffer != "END") this.$bribeGalcopOfficial();
		if (player.ship.docked && player.ship.dockedStation.hasRole("anarchies_renegade_station")) this.$rippedOffByRenegades();
		// you get an explanatory briefing at your first visit on a Hacker Outpost; the fact is stored in mission_anarchies_multipass
		if (player.ship.docked && player.ship.dockedStation.hasRole("anarchies_hacker_outpost") && !missionVariables.anarchies_multipass) {
			mission.runScreen({ screenID: "anarchies_hacker", titleKey: "Anarchies_Hacker_Outpost_Welcome", messageKey: "Anarchies_Hacker_Outpost_Briefing" });
			missionVariables.anarchies_multipass = "YES";
		}
		if (player.ship.docked) this.$amnestyOffer();
	}
}

this.guiScreenWillChange = function (to, from) {
	if (to == "GUI_SCREEN_MANIFEST") this.$displayTimeTillAmnestyDeadline();
}

this.guiScreenChanged = function (to, from) {
	// a corrupt GalCop official turns up only in Anarchy main stations, in the equipment department, if the player has enough money and isn't clean, and by chance
	if (player.ship.docked && player.ship.dockedStation.isMainStation && system.government == 0 && !missionVariables.offering && player.bounty > 0 && player.credits >= 12000 && to == "GUI_SCREEN_EQUIP_SHIP" && Math.random() > 0.86) this.$bribeGalcopOfficial();
}

this.playerBoughtEquipment = function (equipmentKey) {
	// the processes are now run immediately after buying the equipment, independent from the next tickle
	if (equipmentKey == "EQ_MULTIPASS") this.$multipass();
	if (equipmentKey == "EQ_MULTIPASSED_ID") this.$multipassedID();
	// if you buy a used Naval energy unit while you still have the normal one, Oolite 1.72 would remove your expensive new purchase again
	// we don't want that, so we're removing the standard unit, and everything's fine
	if (equipmentKey == "EQ_NAVAL_ENERGY_UNIT_SALVAGED") {
		player.ship.removeEquipment("EQ_ENERGY_UNIT");
		// the extra energy unit is also removed from the list of bought salvaged equipment, if it should have been listed there
		missionVariables.anarchies_salvaged_equipment = missionVariables.anarchies_salvaged_equipment & (Math.pow(2, this.equipment_key.length) - 1 - Math.pow(2, 4));
	}
	// any used equipment is installed properly right away
	if (equipmentKey.substr(equipmentKey.length - 9, 9) === "_SALVAGED") this.$salvagedEquipmentBought(equipmentKey);
}

this.shipWillLaunchFromStation = function (station) {
	if (station.hasRole("anarchies_salvage_gang")) this.$resetSalvagedEquipmentChances();
	this.$addBountyHunters();
	this.$setUpSystem(false);
	this.equipmentFailureTimer.start();
}

this.shipTargetDestroyed = function (target) {
	// the bonus on your own bounty if you have killed a pirate is currently disabled,
	// because Oolite is doing this all on its own since version 1.74
	// this.$bonusOnBounty(target);
	// when fighting a battle to get amnesty, your kills are counted
	if (system.isInterstellarSpace && missionVariables.anarchies_amnesty == "FIGHT_IN_BATTLE" && target.hasRole("thargoid") && !player.ship.isCloaked) {
		this.thargoidsKilled++
		missionVariables.anarchies_amnesty_thargoids_killed = this.thargoidsKilled;
		if (this.thargoidsKilled <= 4) {
			player.consoleMessage(expandMissionText("Anarchies_amnesty_" + this.thargoidsKilled + "_killed"));
			mission.setInstructionsKey("Anarchies_amnesty_" + this.thargoidsKilled + "_killed");
		}
	}
}

this.shipLaunchedEscapePod = function (escapepod) {
	// stores the player's bounty in a script variable
	this.playerBounty = player.bounty;
	// remembers use of escape pod
	this.escapePodUsed = true;
}

this.systemWillPopulate = function () { //for Oolite 1.79 and above
	this.$setUpSystem(true);
}