"use strict";
this.name = "GalCopBB_Defend";
this.author = "phkb";
this.copyright = "2018 phkb";
this.description = "Controls all defend missions (150-159)";
this.license = "CC BY-NC-SA 4.0";

/*
	*150 - defend anaconda group from pirates
	151 - defend liner from pirates (need liners OXP installed)
		>> need to check if any liners spawned - if so, pick one and make it the target
				Q: how to tell one liner is the target ship?
			if not, spawn one and make it the target
	*152 - defend main station from pirates
		>> make mission text relate to "pirates attempting to disrupt station operations"
		>> need a longer play on this one 
			where did the station killer missile come from? how did the pirates come to have it?
	153 - defend other galcop station from pirates (OXP required)
		> how to tell what stations will be available at destination? or only tell player on arrival in system
	*154 - defend main station from thargoids
	155 - defend other station from thargoids (OXP's required)
		> how to tell what stations will be available at destination? or only tell player on arrival in system
	*156 - defend witchpoint beacon from thargoids
*/

this._spawnTimer = null; // timer to control for how long attackers will be spawned
this._defenceTarget = null; // object reference to defence target
this._attackers = []; // array of ships who are attacking target
this._playerCount = 0; // number of attacking ships destroyed by player
this._playerMissileCount = 0; // number of station killer missiles destroyed by player
this._endTime = 0; // time at which the attack will cease
this._monitorTimer = null; // timer to monitor when attack is finished
this._endTimeRemaining = 0; // holding value of time remaining if player saves game at station during an attack
this._missionTypeActive = 0; // holding value of active mission type if player saves game at station during attack
this._startTimer = null; // timer to check for when the mission will start
this._setupAttackerTarget = null; // timer to control the setup of attacker targets in the AI
this._alertMissileTimer = null; // timer to control an alert to the player when a station killer missile is launched

this._initConfig = {
	150: { spawnCount: 3, initialSpawnTime: 30, reSpawnTime: 60, attackPeriod: 300, maxCount: 5 },
	152: { spawnCount: 5, initialSpawnTime: 30, reSpawnTime: 15, attackPeriod: 600, maxCount: 12 },
	154: { spawnCount: 3, initialSpawnTime: 45, reSpawnTime: 30, attackPeriod: 600, maxCount: 8 },
	156: { spawnCount: 2, initialSpawnTime: 45, reSpawnTime: 60, attackPeriod: 300, maxCount: 3 },
};

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	var gcm = worldScripts.GalCopBB_Missions;
	// add these mission types into the main control
	var list = [150, 152, 154, 156];
	//if (worldScripts["liners_populator_script.js"]) list.push(151);
	gcm._availableMissionTypes = gcm._availableMissionTypes.concat(list);

	// if we're in the middle of a 152/154 mission, set it up again
	if (missionVariables.GalCopBBMissions_DefendActive) {
		this._defenceTarget = system.mainStation;
		this._missionTypeActive = missionVariables.GalCopBBMissions_DefendActive;
		this._endTimeRemaining = missionVariables.GalCopBBMissions_EndTimeRemaining;
		this._playerCount = missionVariables.GalCopBBMissions_PlayerCount;
		this._playerMissileCount = missionVariables.GalCopBBMissions_PlayerMissileCount;
		// there should only be one
		var missList = gcm.$getListOfMissions(true, [152, 154]);
		for (var i = 0; i < missList.length; i++) {
			var item = missList[i];
			if (item.destination === system.ID &&
				item.expiry > clock.adjustedSeconds &&
				item.data.quantity < item.data.targetQuantity) {

				this._defenceTarget.script._missionID = item.ID;
				this._defenceTarget.script._missionType = this._missionTypeActive;
				this._defenceTarget.script._attackerCount = 0;
			}
		}
		this.shipWillDockWithStation = this.$gcm_shipWillDockWithStation;
		this.shipLaunchedFromStation = this.$gcm_shipLaunchedFromStation;

		delete missionVariables.GalCopBBMissions_DefendActive;
		delete missionVariables.GalCopBBMissions_EndTimeRemaining;
		delete missionVariables.GalCopBBMissions_PlayerCount;
		delete missionVariables.GalCopBBMissions_PlayerMissileCount;
		this._missionTypeActive = 0;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function () {
	var list = worldScripts.GalCopBB_Missions.$getListOfMissions(true, [150, 152, 154, 156]);
	if (list.length > 0) {
		for (var i = 0; i < list.length; i++) {
			var item = list[i];

			if (item.data.missionType === 150 && item.destination === system.ID &&
				item.data.quantity === 0 &&
				item.data.destroyedQuantity === 0 &&
				item.expiry > clock.adjustedSeconds) {

				// start monitoring for player getting to station
				this.shipWillDockWithStation = this.$gcm_shipWillDockWithStation2;

				var checkShips = system.addShips("[anaconda]", 1, Vector3D(0, 0, 0.2).fromCoordinateSystem("wpu"), 1000);
				var s = null;
				if (checkShips) s = checkShips[0];
				if (s) {
					//log(this.name, "anaconda target vessel created successfully");
					var g = worldScripts.GalCopBB_Missions;
					if (g._rsnInstalled) s.shipUniqueName = item.data.targetShipName;

					s.setScript("oolite-default-ship-script.js");
					s.script._missionID = item.ID;
					s.script._missionType = item.data.missionType;
					s.script._attackerCount = 0;
					// attach scripts
					if (s.script.shipDied) s.script.$gcm_hold_shipDied = s.script.shipDied;
					s.script.shipDied = this.$gcm_shipDied;
					if (s.script.shipWillEnterWormhole) s.script.$gcm_hold_shipWillEnterWormhole = s.script.shipWillEnterWormhole;
					s.script.shipWillEnterWormhole = this.$gcm_shipWillEnterWormhole;

					s.switchAI("oolite-shuttleAI.js");

					s.script.$initialise = this.$initialise;
					s.script.$setDestination = this.$setDestination;
					s.script.$initialise();
					s.destination = system.mainStation;
					s.destinationSystem = system.ID;
					s.fuel = 0; // prevent any chance of jumping out

					this._defenceTarget = s;
					this._playerCount = 0;
					this._playerMissileCount = 0;
					this._startTimer = new Timer(this, this.$startAttack, 10, 10);
				} else {
					log(this.name, "ERROR: anaconda target vessel not created");
				}
			}

			if ((item.data.missionType === 152 || item.data.missionType === 154) && item.destination === system.ID &&
				item.data.quantity === 0 &&
				item.data.destroyedQuantity === 0 &&
				item.expiry > clock.adjustedSeconds) {

				// start monitoring for player getting to station
				this.shipEnteredStationAegis = this.$gcm_shipEnteredStationAegis;
				this.shipWillDockWithStation = this.$gcm_shipWillDockWithStation;
				// for testing only
				//system.mainStation.script.shipTakingDamage = this.$station_shipTakingDamage;

				this._defenceTarget = system.mainStation;
				this._defenceTarget.script._missionID = item.ID;
				this._defenceTarget.script._missionType = item.data.missionType;
				this._defenceTarget.script._attackerCount = 0;
				// attach scripts
				if (this._defenceTarget.script.shipDied) this._defenceTarget.script.$gcm_hold_shipDied = this._defenceTarget.script.shipDied;
				this._defenceTarget.script.shipDied = this.$gcm_shipDied;
				this._playerCount = 0;
				this._playerMissileCount = 0;
			}

			if (item.data.missionType === 156 && item.destination === system.ID &&
				item.data.quantity === 0 &&
				item.data.destroyedQuantity === 0 &&
				item.expiry > clock.adjustedSeconds) {
				var ships = system.shipsWithRole("buoy-witchpoint");
				if (ships.length > 0) {
					this._defenceTarget = ships[0];
					// bump up the energy stats a bit so the player has a chance to defend something
					this._defenceTarget.maxEnergy = 1000;
					this._defenceTarget.energy = 1000;
					this._defenceTarget.script._missionID = item.ID;
					this._defenceTarget.script._missionType = item.data.missionType;
					this._defenceTarget.script._attackerCount = 0;
					// attach scripts
					if (this._defenceTarget.script.shipDied) this._defenceTarget.script.$gcm_hold_shipDied = this._defenceTarget.script.shipDied;
					this._defenceTarget.script.shipDied = this.$gcm_shipDied;
					var conf = this._initConfig[item.data.missionType];
					this._spawnTimer = new Timer(this, this.$spawnThargoidAttackers, conf.initialSpawnTime, conf.reSpawnTime);
					this._endTime = clock.adjustedSeconds + conf.attackPeriod; // 5 minutes
					this._monitorTimer = new Timer(this, this.$monitorAttackers, 30, 30);
					this._playerCount = 0;
					this._playerMissileCount = 0;
				}
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	if (this._missionTypeActive != 0) {
		missionVariables.GalCopBBMissions_DefendActive = this._missionTypeActive;
		missionVariables.GalCopBBMissions_EndTimeRemaining = this._endTimeRemaining;
		missionVariables.GalCopBBMissions_PlayerCount = this._playerCount;
		missionVariables.GalCopBBMissions_PlayerMissileCount = this._playerMissileCount;
	}
}

//-------------------------------------------------------------------------------------------------------------
// for mission types 152/154
this.$gcm_shipWillDockWithStation = function $gcm_shipWillDockWithStation(station) {
	// is the spawn timer still running?
	if (this._spawnTimer && this._spawnTimer.isRunning && this._endTime > clock.seconds) {
		// calc home much time is still to run
		this._endTimeRemaining = this._endTime - clock.seconds;
		this._missionTypeActive = this._defenceTarget.script._missionType;
		// stop, but don't delete, the timers
		this._spawnTimer.stop();
		this._monitorTimer.stop();
		// set up the relaunch 
		this.shipLaunchedFromStation = this.$gcm_shipLaunchedFromStation;
	}
	delete this.shipWillDockWithStation;
}

//-------------------------------------------------------------------------------------------------------------
// for mission types 150
this.$gcm_shipWillDockWithStation2 = function $gcm_shipWillDockWithStation2(station) {
	// is any timers running?
	if ((this._startTimer && this._startTimer.isRunning) || (this._spawnTimer && this._spawnTimer.isRunning)) {
		// fail mission by destroying the defence ship
		this._defenceTarget.explode();
		this.$stopTimers();
	}
	delete this.shipWillDockWithStation;
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_shipLaunchedFromStation = function $gcm_shipLaunchedFromStation(station) {
	this._endTime = clock.adjustedSeconds + this._endTimeRemaining;
	if (this._spawnTimer == null) {
		var conf = this._initConfig[this._defenceTarget.script._missionType];
		conf.initialSpawnTime = 2;
		switch (this._defenceTarget.script._missionType) {
			case 152:
				this._spawnTimer = new Timer(this, this.$spawnPirateAttackers, conf.initialSpawnTime, conf.reSpawnTime);
				break;
			case 154:
				this._spawnTimer = new Timer(this, this.$spawnThargoidAttackers, conf.initialSpawnTime, conf.reSpawnTime);
				break;
		}
		this._monitorTimer = new Timer(this, this.$monitorAttackers, 30, 30);
	} else {
		this._spawnTimer.start();
		this._monitorTimer.start();
	}

	delete this.shipLaunchedFromStation;
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_shipEnteredStationAegis = function $gcm_shipEnteredStationAegis(station) {
	if (station.isValid && station.isInSpace && station == this._defenceTarget) {
		var conf = this._initConfig[this._defenceTarget.script._missionType];
		switch (this._defenceTarget.script._missionType) {
			case 152:
				this._spawnTimer = new Timer(this, this.$spawnPirateAttackers, conf.initialSpawnTime, conf.reSpawnTime);
				break;
			case 154:
				this._spawnTimer = new Timer(this, this.$spawnThargoidAttackers, conf.initialSpawnTime, conf.reSpawnTime);
				break;
		}
		this._endTime = clock.adjustedSeconds + conf.attackPeriod; // 5 minutes
		this._monitorTimer = new Timer(this, this.$monitorAttackers, 30, 30);
		delete this.shipEnteredStationAegis;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function (cause, destination) {
	this.$stopTimers();
}

//-------------------------------------------------------------------------------------------------------------
this.shipDied = function (whom, why) {
	this.$stopTimers();
}

//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedEscapePod = function (escapepod) {
	this.$stopTimers();
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_shipDied = function $gcm_shipDied(whom, why) {
	if (this.ship.script.$gcm_hold_shipDied) this.ship.script.$gcm_hold_shipDied(whom, why);
	if (this.ship.script._setDest && this.ship.script._setDest.isRunning) this.ship.script._setDest.stop();
	//log(this.name, "target ship died " + why + ", " + whom);
	if (this.ship.script._missionID && this.ship.script._missionID > 0) {
		var bb = worldScripts.BulletinBoardSystem;
		var item = bb.$getItem(this.ship.script._missionID);
		if (item.percentComplete < 1) {
			item.data.destroyedQuantity = 1;
			switch (this.ship.script._missionType) {
				case 150:
					player.consoleMessage(expandDescription("[gcm_anaconda_died]"), 5);
					break;
				case 152:
				case 154:
					player.consoleMessage(expandDescription("[gcm_station_died]"), 5);
					break;
				case 156:
					player.consoleMessage(expandDescription("[gcm_beacon_died]"), 5);
					break;
			}
			if (item.data.quantity === 1) {
				item.data.quantity = 0;
				player.consoleMessage(expandDescription("[goal_updated]"));
			}
		}
		worldScripts.GalCopBB_Defend.$stopTimers();
		this.ship.script._missionID = 0;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_shipWillEnterWormhole = function $gcm_shipWillEnterWormhole() {
	if (this.ship.script.$gcm_hold_shipWillEnterWormhole) this.ship.script.$gcm_hold_shipWillEnterWormhole();
	if (this.ship.script._setDest && this.ship.script._setDest.isRunning) this.ship.script._setDest.stop();
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_badGuy_shipDied = function $gcm_badGuy_shipDied(whom, why) {
	if (this.ship.script.$gcm_hold_shipDied) this.ship.script.$gcm_hold_shipDied(whom, why);
	if (this.ship.script._missionID && this.ship.script._missionID > 0) {
		this.ship.script.$processBadGuyEnd(whom);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_badGuy_shipTakingDamage = function $gcm_badGuy_shipTakingDamage(amount, whom, type) {
	if (this.ship.script.$gcm_hold_shipTakingDamage) this.ship.script.$gcm_hold_shipTakingDamage(amount, whom, type);
	// make a note of who was our last attacker, so we can tell if the pilot ejects
	// may not always correspond to the correct ship, but it's close enough
	this.ship.script._attackedByWhom = whom;
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_badGuy_shipLaunchedEscapePod = function $gcm_badGuy_shipLaunchedEscapePod(escapePod) {
	if (this.ship.script.$gcm_hold_shipLaunchedEscapePod) this.ship.script.$gcm_hold_shipLaunchedEscapePod(escapePod);
	if (this.ship.script._missionID && this.ship.script._missionID > 0) {
		// disable the shipDied function
		delete this.ship.script.shipDied;
		if (this.ship.script.$gcm_hold_shipDied) {
			this.ship.script.shipDied = this.ship.script.$gcm_hold_shipDied;
			delete this.ship.script.$gcm_hold_shipDied;
		}
		this.ship.script.$processBadGuyEnd(this.ship.script._attackedByWhom);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$processBadGuyEnd = function $processBadGuyEnd(whom) {
	this.ship.script._missionID = 0;
	var w = worldScripts.GalCopBB_Defend;
	if (w._defenceTarget && w._defenceTarget.isValid && w._defenceTarget.isInSpace) {
		w._defenceTarget.script._attackerCount -= 1;
		if (whom && whom.isPlayer) w._playerCount += 1;
		if (w._defenceTarget.script._attackerCount <= 0 && w._spawnTimer === null) {
			// yay!
			w.$finishMission(w._defenceTarget.script._missionID, (this.ship.isThargoid ? 500 : 200));
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$stopTimers = function $stopTimers() {
	if (this._monitorTimer && this._monitorTimer.isRunning) this._monitorTimer.stop();
	this._monitorTimer = null;
	if (this._spawnTimer && this._spawnTimer.isRunning) this._spawnTimer.stop();
	this._spawnTimer = null;
	if (this._startTimer && this._startTimer.isRunning) this._startTimer.stop();
	this._startTimer = null;
}

//-------------------------------------------------------------------------------------------------------------
this.$spawnThargoidAttackers = function $spawnThargoidAttackers() {
	if (this._defenceTarget.isValid && this._defenceTarget.isInSpace) {
		var tgt = this._defenceTarget;
		var g = worldScripts.GalCopBB_Missions;
		var conf = this._initConfig[tgt.script._missionType];
		if (Math.random() > 0.5 && tgt.script._attackerCount < conf.maxCount) {
			// get the initial number of ships to spawn from the config
			var num = conf.spawnCount;
			var pos = null;
			var range = 0;
			switch (tgt.script._missionType) {
				case 154:
					pos = g.$findPosition(tgt.position);
					range = 200;
					break;
				case 156:
					pos = tgt.position;
					range = 25000;
					break;
			}
			var thargoids = system.addShips("thargoid", num, pos, range);
			// reset the count to 1 for any future respawns
			conf.spawnCount = 1;
			// make sure all the attackers have the target
			for (var i = 0; i < thargoids.length; i++) {
				this._attackers.push(thargoids[i]);
				thargoids[i].script._missionID = tgt.script._missionID;
				// attach scripts
				if (thargoids[i].script.shipDied) thargoids[i].script.$gcm_hold_shipDied = thargoids[i].script.shipDied;
				thargoids[i].script.shipDied = this.$gcm_badGuy_shipDied;
				// attach completion script
				thargoids[i].script.$processBadGuyEnd = this.$processBadGuyEnd;

				tgt.script._attackerCount += 1;
				thargoids[i].target = tgt;
				thargoids[i].performAttack();
			}
		}
		if (clock.adjustedSeconds > this._endTime) {
			this._spawnTimer.stop();
			this._spawnTimer = null;
			// have we finished?
			if (tgt.script._attackerCount <= 0) {
				// yay!
				this.$finishMission(tgt.script._missionID, 500);
			}
		}
	} else {
		this._spawnTimer.stop();
		this._spawnTimer = null;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$spawnPirateAttackers = function $spawnPirateAttackers() {
	if (this._defenceTarget.isValid && this._defenceTarget.isInSpace) {
		var tgt = this._defenceTarget;
		var g = worldScripts.GalCopBB_Missions;
		var conf = this._initConfig[tgt.script._missionType];
		if (Math.random() > 0.5 && tgt.script._attackerCount < conf.maxCount) {
			// get the initial number of ships to spawn from the config
			var pop = worldScripts["oolite-populator"];
			var pos = g.$findPosition(tgt.position);
			var types = ["pirate-medium-fighter", "pirate-medium-fighter", "pirate-heavy-fighter"];
			var pirates = system.addShips(types[Math.floor(Math.random() * types.length)], conf.spawnCount, pos, 200);
			if (pirates) {
				// decrease the count by 1 for any future respawns
				conf.spawnCount -= 1;
				if (conf.spawnCount < 1) conf.spawnCount = 1;
				// make sure all the attackers are equipped, have names, and bounties
				for (var i = 0; i < pirates.length; i++) {
					var pr = pirates[i];
					if (g._rsnInstalled) pr.shipUniqueName = g.$getRandomShipName(pr, "pirate");
					pr.setScript("oolite-default-ship-script.js");
					pr.script._configDone = false;
					// configure our attackers
					pr.setBounty(60 + system.government + Math.floor(Math.random() * 8), "setup actions");
					pr.setCrew({
						name: randomName() + " " + randomName(),
						bounty: pr.bounty,
						insurance: 0
					});
					if (pr.hasHyperspaceMotor) {
						pop._setWeapons(pr, 1.75); // bigger ones sometimes well-armed
					} else {
						pop._setWeapons(pr, 1.3); // rarely well-armed
					}
					pop._setSkill(pr, 4 - system.info.government);
					if (Math.random() * 16 < system.info.government) pop._setMissiles(pr, -1);
					pr.script._missionID = tgt.script._missionID;
					// attach monkey scripts
					if (pr.script.shipDied) pr.script.$gcm_hold_shipDied = pr.script.shipDied;
					pr.script.shipDied = this.$gcm_badGuy_shipDied;
					if (pr.script.shipLaunchedEscapePod) pr.script.$gcm_hold_shipLaunchedEscapePod = pr.script.shipLaunchedEscapePod;
					pr.script.shipLaunchedEscapePod = this.$gcm_badGuy_shipLaunchedEscapePod;
					if (pr.script.shipTakingDamage) pr.script.$gcm_hold_shipTakingDamage = pr.script.shipTakingDamage;
					pr.script.shipTakingDamage = this.$gcm_badGuy_shipTakingDamage;
					// attach completion script
					pr.script.$processBadGuyEnd = this.$processBadGuyEnd;

					tgt.script._attackerCount += 1;
					// make sure the AI is switched
					pr.switchAI("gcm-attackerAI.js");
					this._attackers.push(pr);
				}
				// get ready to set up the target
				if (this._setupAttackerTarget == null || this._setupAttackerTarget.isRunning === false) {
					this._setupAttackerTarget = new Timer(this, this.$giveAttackersTarget, 1, 0);
				}
			}
		}
		if (clock.adjustedSeconds > this._endTime) {
			this._spawnTimer.stop();
			this._spawnTimer = null;
			// have we finished?
			if (tgt.script._attackerCount <= 0) {
				// yay!
				this.$finishMission(tgt.script._missionID, 200);
			}
		}
	} else {
		this._spawnTimer.stop();
		this._spawnTimer = null;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$startAttack = function $startAttack() {
	var p = player.ship;
	var tgt = this._defenceTarget;
	if (p.position.distanceTo(tgt) < 24000) {
		this._startTimer.stop();
		this._startTimer = null;
		// add a beacon to the ship for easy navigation
		tgt.beaconCode = "A";
		tgt.beaconLabel = expandDescription("[gcm_anaconda_transport]");
		player.consoleMessage(expandDescription("[gcm_anaconda_located]"), 5);
		tgt.commsMessage(expandDescription("[gcm_anaconda_greeting]"), player.ship);

		// start time
		var conf = this._initConfig[tgt.script._missionType];
		this._spawnTimer = new Timer(this, this.$spawnPirateAttackers, conf.initialSpawnTime, conf.reSpawnTime);
		this._endTime = clock.adjustedSeconds + conf.attackPeriod; // 5 minutes
		this._monitorTimer = new Timer(this, this.$monitorAttackers, 30, 30);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$monitorAttackers = function $monitorAttackers() {
	if (this._defenceTarget.isValid === false || this._defenceTarget.isInSpace === false) {
		this._monitorTimer.stop();
		this._monitorTimer = null;
		return;
	}
	var tgt = this._defenceTarget;
	// auto-destruct ship if the player gets too far away
	if (player.ship.position.distanceTo(tgt) > 60000) {
		this._monitorTimer.stop();
		this._monitorTimer = null;
		tgt.explode();
		return;
	}
	for (var i = 0; i < this._attackers.length; i++) {
		var atk = this._attackers[i];
		if (atk.isValid && atk.isInSpace) {
			// if they're running away and out of range, remove them
			if (atk.position.distanceTo(tgt.position) > 100000 && atk.position.distanceTo(player.ship) > 100000) {
				atk.remove();
			}
			// make sure all our attackers have their target set
			if (atk.isThargoid === false && atk.AIScript && atk.AIScript.oolite_priorityai && atk.AIScript.oolite_priorityai.getParameter("oolite_attackTarget") != tgt) {
				atk.AIScript.oolite_priorityai.setParameter("oolite_attackTarget", tgt);
				atk.AIScript.oolite_priorityai.configurationResetWaypoint();
				atk.AIScript.oolite_priorityai.reconsiderNow();
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$acceptedMission = function $acceptedMission(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	var gcm = worldScripts.GalCopBB_Missions;

	if (this._debug) log(this.name, "accepted mission id = " + missID);

	if (!item) {
		log(this.name, "!!ERROR: BB returned null value from $getItem on mission ID " + missID);
		return;
	}
	gcm.$updateLastMissionDate(item.source, item.data.missionType);
}

//-------------------------------------------------------------------------------------------------------------
this.$completedMission = function $completedMission(missID) {
	var p = player.ship;
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	var gcm = worldScripts.GalCopBB_Missions;

	if ([152, 154, 156].indexOf(item.data.missionType) >= 0) {
		// if the player has a bounty, doing these missions will reduce it
		if (player.ship.bounty > 0) {
			for (var i = 0; i < 10; i++) {
				if (player.ship.bounty > 0) player.ship.setBounty(player.ship.bounty - 1, "community service");
			}
			player.ship.consoleMessage(expandDescription("[gcm_bounty_reduced]"));
		}
	}
	// update mission history
	gcm.$addMissionHistory(item.source, item.data.missionType, 1, 0);

	gcm.$checkForFollowupMission(item);

	// adjust reputation with entities
	var rep = worldScripts.GalCopBB_Reputation;
	if (rep._disabled === false) {
		rep.$adjustReputationSuccess(item.data.missionType, item.source, item.destination, item.percentComplete);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$confirmCompleted = function $confirmCompleted(missID) {
	var p = player.ship;
	var result = "";
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	if (item) {

	}
	return result;
}

//-------------------------------------------------------------------------------------------------------------
this.$terminateMission = function $terminateMission(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	var sbm = worldScripts.Smugglers_BlackMarket;
	var gcm = worldScripts.GalCopBB_Missions;

	// adjust reputation only when the terminatePenalty flag is set to true 
	if (item.data.terminatePenalty === true) {
		// update mission history
		gcm.$updateFailedHistoryReputation(item);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$failedMission = function $failedMission(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var sbm = worldScripts.Smugglers_BlackMarket;
	var gcm = worldScripts.GalCopBB_Missions;
	var eq = "";

	if (!sbm) {
		// if there's no blackmarket option, just remove the equipment
		this.$terminateMission(missID);
		return;
	}

	var item = bb.$getItem(missID);
	// update mission history
	gcm.$updateFailedHistoryReputation(item);

	// cleanup any residual attackers
	for (var i = 0; i < this._attackers.length; i++) {
		this._attackers[i].remove(true);
	}

	if (eq != "") gcm._equipmentFromFailedMissions.push({
		missionType: item.data.missionType,
		source: item.source,
		equip: eq,
		date: clock.adjustedSeconds
	});
}

//-------------------------------------------------------------------------------------------------------------
this.$updateManifestEntry = function $updateManifestEntry(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	if (item.data.destroyedQuantity === 0) {
		var gcm = worldScripts.GalCopBB_Missions;
		gcm.$updateManifestEntry(missID);
	} else {
		var rep = worldScripts.GalCopBB_Reputation;
		// update the manifest
		bb.$updateBBManifestText(
			missID,
			rep.$transformText(expandDescription("[missionType" + item.data.missionType + "_failedManifest]", {
				system: System.systemNameForID(item.source),
				expiry: ""
			}), item.source, item.destination)
		);
		bb.$updateBBStatusText(
			missID,
			rep.$transformText(expandDescription("[missionType" + item.data.missionType + "_failedStatus]", {
				system: System.systemNameForID(item.source)
			}), item.source, item.destination)
		);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$missionAvailability = function $missionAvailability(missID, missType, origSysID) {
	return worldScripts.GalCopBB_Missions.$missionAvailability(missID, missType, origSysID);
}

//-------------------------------------------------------------------------------------------------------------
// 150 - defend transport from pirates
this.$missionType150_Values = function $missionType150_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 1;
	result["price"] = parseInt((parseInt(Math.random() * 1300) + 1300) / 10) * 10 + (7 - destSysInfo.government) * 20 + missValues.$calcDistanceBonus(routeDistance, 10);
	result["expiry"] = clock.adjustedSeconds + routeTime + 1800;
	result["penalty"] = 0;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
// 152 - defend main station from pirates
this.$missionType152_Values = function $missionType152_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 1;
	result["price"] = parseInt((parseInt(Math.random() * 1000) + 1000) / 10) * 10 + (7 - destSysInfo.government) * 20 + missValues.$calcDistanceBonus(routeDistance, 10);
	result["expiry"] = clock.adjustedSeconds + routeTime + 1800;
	result["penalty"] = 0;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
// 154 - defend main station from thargoids
this.$missionType154_Values = function $missionType154_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 1;
	result["price"] = parseInt((parseInt(Math.random() * 1500) + 1500) / 10) * 10 + (7 - destSysInfo.government) * 20 + missValues.$calcDistanceBonus(routeDistance, 10);
	result["expiry"] = clock.adjustedSeconds + routeTime + 1800;
	result["penalty"] = 0;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
// 156 - defend wp beacon from thargoids
this.$missionType156_Values = function $missionType156_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 1;
	result["price"] = parseInt((parseInt(Math.random() * 1500) + 1500) / 10) * 10 + (7 - destSysInfo.government) * 20 + missValues.$calcDistanceBonus(routeDistance, 10);
	result["expiry"] = clock.adjustedSeconds + routeTime + 1800;
	result["penalty"] = 0;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
this.$finishMission = function (missID, bonusType) {
	var gcm = worldScripts.GalCopBB_Missions;
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	if (item.data.quantity === 0) {
		// send a message to the player (types 150,152,154 only)
		if (this._defenceTarget && this._defenceTarget.isValid && this._defenceTarget.isInSpace) {
			if ([150, 152, 154].indexOf(this._defenceTarget.script._missionType) >= 0 && player.ship.position.distanceTo(this._defenceTarget) < player.ship.scannerRange) {
				this._defenceTarget.commsMessage(expandDescription("[gcm_defend_finish_comms]"), player.ship);
			}
		}

		item.data.quantity += 1;
		bb.$updateBBMissionPercentage(item.ID, 1);
		gcm.$logMissionData(item.ID);
		// add bonus for any ships killed
		item.payment += (bonusType * this._playerCount);
		// add bonus for any station killer missiles destroyed
		if (this._playerMissileCount > 0) item.payment += (500 * this._playerMissileCount);
		// reset variables
		this._playerCount = 0;
		this._playerMissileCount = 0;
		player.consoleMessage(expandDescription("[goal_updated]"));
	}
}

//-------------------------------------------------------------------------------------------------------------
// attached to a spawned anaconda vessel to force the main station to be the destination
this.$initialise = function $initialise() {
	// run a timer so the ai controller has a chance to startup before we try and set the destination
	this.ship.script._retryCounter = 0;
	this.ship.script._setDest = new Timer(this, this.ship.script.$setDestination, 2, 0);
}

//-------------------------------------------------------------------------------------------------------------
this.$setDestination = function $setDestination() {
	// do we have a ai controller yet?
	if (!this.ship.AIScript || !this.ship.AIScript.oolite_priorityai) {
		// start counting after ship is in flight
		if (this.ship.status !== "STATUS_IN_FLIGHT") this.ship.script._retryCounter += 1;
		// give up after ten tries
		if (this.ship.script._retryCounter === 10) return;

		// otherwise, reset the timer and try again later
		delete this.ship.script._setDest;
		this.ship.script._setDest = new Timer(this, this.ship.script.$setDestination, 2, 0);
		return;
	}

	this.ship.AIScript.oolite_priorityai.setParameter("oolite_selectedPlanet", null);
	this.ship.AIScript.oolite_priorityai.setParameter("oolite_selectedStation", system.mainStation);
	this.ship.AIScript.oolite_priorityai.reconsiderNow();
	delete this.ship.script._setDest;
}

//-------------------------------------------------------------------------------------------------------------
// makes the attackers attack a particular target
this.$giveAttackersTarget = function $giveAttackersTarget() {
	var retry = false;
	for (var i = 0; i < this._attackers.length; i++) {
		var shp = this._attackers[i];
		if (shp.AIScript && shp.AIScript.oolite_priorityai) {
			if (shp.script._configDone === false) {
				shp.AIScript.oolite_priorityai.setParameter("oolite_pirateLurk", this._defenceTarget.position.add(this._defenceTarget.vectorForward.multiply(5000)));
				shp.AIScript.oolite_priorityai.setParameter("oolite_attackTarget", this._defenceTarget);
				shp.AIScript.oolite_priorityai.configurationResetWaypoint();
				shp.AIScript.oolite_priorityai.reconsiderNow();
				if (shp.missileCapacity > 0) {
					shp.script.$attackStation = this.$attackStation;
					shp.script._attackTimer = new Timer(shp, shp.script.$attackStation, 5, 5);
				}
				shp.script._configDone = true;
			}
		} else {
			retry = true;
			break;
		}
		if (retry === true) break;
	}
	if (retry === true) {
		this._setupAttackerTarget = new Timer(this, this.$giveAttackersTarget, 1, 0);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$station_shipTakingDamage = function (amount, whom, type) {
	log(this.name, "station hit - " + amount + ", " + type + ": " + this.ship.energy);
}

//-------------------------------------------------------------------------------------------------------------
this.$attackStation = function $attackStation() {
	if (!this) return;
	if (!system.mainStation) {
		this.script._attackTimer.stop();
		return;
	}
	var num = system.countShipsWithRole("EQ_GCM_STATIONKILLER_MISSILE");
	var dist = this.position.distanceTo(system.mainStation);
	if (num < 2 && dist > 18000 && dist < 23000 && Math.random() > 0.5) {
		this.script._attackTimer.stop();
		// target station
		var holdTarget = this.target;
		this.target = system.mainStation;
		// load up the station killer missile
		// make sure there's a free slot
		if (this.missiles.length == this.missileCapacity) {
			this.removeEquipment(this.missiles[this.missiles.length - 1].equipmentKey);
		}
		var result = this.awardEquipment("EQ_GCM_STATIONKILLER_MISSILE");
		if (result) {
			var ms = this.fireMissile("EQ_GCM_STATIONKILLER_MISSILE");
			if (ms.script.shipDied) ms.script.$gcm_hold_shipDied = ms.script.shipDied;
			ms.script.shipDied = this.$gcm_missile_shipDied;

			var gcd = worldScripts.GalCopBB_Defend;
			if (gcd._alertMissileTimer == null || gcd._alertMissileTimer.isRunning === false) {
				gcd._alertMissileTimer = new Timer(gcd, gcd.$missileAlert, 2, 10);
			}
		}
		this.target = holdTarget;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$missileAlert = function $missileAlert() {
	var num = system.countShipsWithRole("EQ_GCM_STATIONKILLER_MISSILE");
	if (num > 0 && player.ship.position.distanceTo(system.mainStation) < player.ship.scannerRange) {
		system.mainStation.commsMessage(expandDescription("[gcm_station_missile_alert]"), player.ship);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_missile_shipDied = function $gcm_missile_shipDied(whom, why) {
	if (this.ship.script.$gcm_hold_shipDied) this.$gcm_hold_shipDied(whom, why);
	if (whom && whom.isPlayer) {
		var w = worldScripts.GalCopBB_Defend;
		w._playerMissileCount += 1;
	}
}