"use strict";
this.name = "GalCopBB_HitTeams";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Controls movement and deployment of hit teams";
this.license = "CC BY-NC-SA 4.0";

/*
99 times out of 100 this code will never run. If players do the missions as prescribed, even if they fail the mission, everything will be fine and we'll never get here.

We will only get here if the player attempted to sell on the black market any special equipment or cargo given to them as part of the mission. Players will have to go out of their way to experience this code.
However, with "open"-style play, the player should have the freedom to do whatever they want, and that's where this code comes into play. The player can sell the equipment, but if they do the source system is likely to be quite upset and send a "hit team" after them.

Hit teams will be larger than a normal assassin team, 8-15 ships based on type of mission, as an incentive to do the right thing.

If the hit team is spawned, and the player manages to evade them, they will keep turning up at each witchpoint until they are all destroyed

process overview
1. set up team size, ship config, location, and maxPursuitDistance (how far they will go in pursuit)
	distance is decided upon based on the projected impact of the betrayal.
		selling cargo is low, selling medical supplies or virus samples is high
		
2. set up the process of movement, executed with each witchspace jump by the player
	head to the players last known position via the fastest route.
	ie. a. get a route from player system to team's current position optimised for time
		b. based on the time taken by the players jump, work out which system the team will reach
		c. if the team hits their travel limit, drop the team from pursuit and just put them at the witchpoint of the source system, waiting for the player's arrival sometime in the future. 
			Player will be flagged as a fugitive in this system
		d. if the team can arrive ahead of the player, put the team at the witchpoint with orders to kill. drop the team from the master list - they've served their purpose

TODO: create a F4 interface screen for disposal of equipment from failed missions

*/

this._preferredAssassinShips = [];
this._lastSystem = -1;
this._lastGal = -1;
this._startTime = 0;
this._doArrivalMessages = false;
this._hitTeams = []; // array of dictionary objects defining hit teams
/*		source					(int) system ID of the original mission
		location				(int) system ID of the teams current position
		reason					text string containing reason player is being pursued (eg "selling data on Black Market)
		ships 					[array of ship defs] basic ship definitions for each of the team members (assumuption that index 0 is leader)
									dataKey
									name
									entityPersonality
		distance				(float) current distance travelled
		maxPursuitDistance		(int) how far the team is willing to go to get the player
*/
this._createTeams = []; // array of index values relating to _hitTeams - these teams will be created during system population
this._suppressMissions = []; // array of system ID's and dates where the player is persona non grata therefore no missions
this._warnPlayer = false;
this._warnPlayerSource = 0;
this._warnPlayerCurrent = 0;

this._sellInfo = {
	"DTA_DATA_PACKAGE": {
		cost: 1500,
		description: expandDescription("[gcm_sensitive_data]")
	},
	"DTA_DATA_DELIVERY": {
		cost: 1500,
		description: expandDescription("[gcm_sensitive_data]")
	},
	"DTA_GALCOP_SEC_SOFTWARE": {
		cost: 2000,
		description: expandDescription("[gcm_galcop_monitoring]")
	},
	"EQ_GCM_BLACK_BOX": {
		cost: 800,
		description: expandDescription("[gcm_black_box]")
	},
	"EQ_GCM_STOLEN_SCHEMATICS": {
		cost: 13000,
		description: expandDescription("[gcm_ship_schematics]")
	},
	"EQ_GCM_STOLEN_CODES": {
		cost: 13000,
		description: expandDescription("[gcm_security_codes]")
	},
	"EQ_GCM_STOLEN_DOCUMENTS": {
		cost: 13000,
		description: expandDescription("[gcm_classified_documents]")
	},
	"EQ_GCM_STOLEN_WEAPONDESIGNS": {
		cost: 13000,
		description: expandDescription("[gcm_weapon_designs]")
	},
	"EQ_GCM_MEDICAL_SUPPLIES": {
		cost: 2000,
		description: expandDescription("[gcm_medical_supplies]")
	},
	"EQ_GCM_VIRUS_SPECIMENS": {
		cost: 4000,
		description: expandDescription("[gcm_virus_specimens]")
	},
	"EQ_GCM_WBSA": {
		cost: 5000,
		description: expandDescription("[gcm_wbsa]")
	},
	"EQ_GCM_SOFTWARE": {
		cost: 25000,
		description: expandDescription("[gcm_galcop_security]")
	},
	"EQ_GCM_SOLAR_SCANNER": {
		cost: 1000,
		description: expandDescription("[gcm_solar_scanner]")
	},
	"EQ_GCM_SEISMIC_SCANNER": {
		cost: 1500,
		description: expandDescription("[gcm_seismic_scanner]")
	},
	"EQ_GCM_RECOVERED_CARGO": {
		cost: 2000,
		description: expandDescription("[gcm_parcel]")
	},
};

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	if (missionVariables.GalCopBB_HitTeams) this._hitTeams = JSON.parse(missionVariables.GalCopBB_HitTeams);
	if (missionVariables.GalCopBB_SuppressSystems) this._suppressMissions = JSON.parse(missionVariables.GalCopBB_SuppressSystems);
	this.$populateBlackMarketSaleList();
	this.$getPreferredShipList();
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	if (this._hitTeams.length > 0) missionVariables.GalCopBB_HitTeams = JSON.stringify(this._hitTeams);
	if (this._suppressMissions.length > 0) missionVariables.GalCopBB_SuppressSystems = JSON.stringify(this._suppressMissions);
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function (station) {
	if (worldScripts.GalCopBB_Missions._simulator === true) return;
	this.$populateBlackMarketSaleList();
}

//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function (station) {
	if (worldScripts.GalCopBB_Missions._simulator === true) return;
	if (this._warnPlayer === true) {
		player.addMessageToArrivalReport(expandDescription("[gcm_hitteam_progress]", {
			system: System.systemNameForID(this._warnPlayerSource),
			location: System.systemNameForID(this._warnPlayerCurrent)
		}));
		this._warnPlayer = false;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function (cause, destination) {
	this._lastSystem = system.ID;
	this._lastGal = galaxyNumber;
	this._startTime = clock.seconds;
	this._doArrivalMessages = true;

	// clean up
	for (var i = this._suppressMissions.length - 1; i >= 0; i--) {
		if (this._suppressMissions[i].date < clock.adjustedSeconds) this._suppressMissions.splice(i, 1);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.systemWillPopulate = function () {
	// how'd we get here? gal jump? reset values and exit
	if (this._hitTeams.length === 0) return;
	if (this._lastGal != -1 && this._lastGal != galaxyNumber) {
		this._hitTeams.length = 0;
		this._suppressMissions.length = 0;
		return;
	}
	this._createTeams.length = 0;
	for (var i = this._hitTeams.length - 1; i >= 0; i--) {
		var team_route = System.infoForSystem(galaxyNumber, this._hitTeams[i].location).routeToSystem(system.info, "OPTIMIZED_BY_TIME");
		if (team_route == null) {
			// player has moved to an unreachable location - end the team and flag the player as a fugitive in the source system (if bounty system is installed)
		} else {
			// how long did the player take to jump
			var elapsed_time = clock.adjustedSeconds - this._startTime;
			// how far along the route would would that take the team?
			// add up the time for each jump from start point until the time taken is greater than the elapsed time
			var step_route = null;
			var gaveup = false;
			for (var j = 1; j < team_route.route.length; j++) {
				step_route = System.infoForSystem(galaxyNumber, this._hitTeams[i].location).routeToSystem(System.infoForSystem(galaxyNumber, team_route.route[j]), "OPTIMIZED_BY_TIME");
				var step_time = step_route.time * 3600; // convert to seconds
				if (step_time >= elapsed_time) {
					// this is our destination
					this._hitTeams[i].location = team_route.route[j];
					if (this._hitTeams[i].location === system.ID) {
						this._createTeams.push(i)
					} else {
						if (this._doArrivalMessages === true) {
							// inform the player (sometimes)
							if (Math.random() > 0.6) {
								this._warnPlayer = true;
								this._warnPlayerSource = this._hitTeams[i].source;
								this._warnPlayerCurrent = this._hitTeams[i].location;
							}
						}
					}
					break;
				}
				if (this._hitTeams[i].maxPursuitDistance > 0 && this._hitTeams[i].distance + step_route.distance > this._hitTeams[i].maxPursuitDistance) {
					// they're giving up.
					this._hitTeams.splice(i, 1);
					gaveup = true;
					break;
					// flag the player as a fugitive in the source system (if bounty system is installed)
				}
			}
			if (gaveup === true || !step_route) continue;
			this._hitTeams[i].distance += step_route.distance;
		}
	}
	if (this._createTeams.length > 0) {
		system.setPopulator("gcm-hitteams", {
			priority: 50,
			location: "WITCHPOINT",
			callback: this.$createHitTeams.bind(this)
		});
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$populateBlackMarketSaleList = function $populateBlackMarketSaleList() {
	var p = player.ship;
	var sbm = worldScripts.Smugglers_BlackMarket;
	var gcm = worldScripts.GalCopBB_Missions;

	if (sbm && sbm.$removeWorldScriptItems) {
		// clear out dregs
		sbm.$removeWorldScriptItems("GalCopBB_HitTeams");

		// failed mission sale items
		for (var i = 0; i < gcm._equipmentFromFailedMissions.length; i++) {
			// factor in the age of the item
			var factor = (30 - (clock.adjustedSeconds - gcm._equipmentFromFailedMissions[i].date) / 86400) / 30;
			// and the government of the system we're currently trying to sell in
			factor *= ((7 - system.government) / 5);
			// make sure we're not too crazy
			if (factor < 0.2) factor = 0.2;
			if (factor > 1.5) factor = 1.5;

			var eq = "";
			if (gcm._equipmentFromFailedMissions[i].equip.indexOf("EQ_") >= 0) {
				var sts = p.equipmentStatus(gcm._equipmentFromFailedMissions[i].equip);
				if (sts === "EQUIPMENT_OK" || sts === "EQUIPMENT_DAMAGED") {
					eq = gcm._equipmentFromFailedMissions[i].equip;
				}
			} else {
				eq = gcm._equipmentFromFailedMissions[i].equip;
			}
			if (eq !== "") {
				sbm.$addSaleItem({
					key: eq + ":" + (i * -1),
					text: this._sellInfo[eq].description,
					cost: parseInt(this._sellInfo[eq].cost * factor),
					worldScript: "GalCopBB_HitTeams",
					sellCallback: "$sellItemCallback"
				});
			}
		}

		// active mission-based sale items
		var list = gcm.$getListOfMissions(true);
		for (var i = 0; i < list.length; i++) {
			// only add sale items for primary missions, not chained ones
			if (list[i].data.chained === false) {
				// factor in the government of the system we're currently trying to sell in
				var factor = (7 - system.government) / 5;
				// make sure we're not too crazy
				if (factor < 0.2) factor = 0.2;
				if (factor > 1.5) factor = 1.5;

				var eq = "";
				switch (list[i].data.missionType) {
					case 22:
					case 23:
						if (p.equipmentStatus("EQ_GCM_BLACK_BOX") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_BLACK_BOX") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_BLACK_BOX";
						break;
					case 24:
						if (p.equipmentStatus("EQ_GCM_RECOVERED_CARGO") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_RECOVERED_CARGO") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_RECOVERED_CARGO";
						break;
					case 31: // data cache collection
						// can only sell it if it's been received
						if (list[i].data.quantity === 1 && list[i].data.targetQuantity > 0) eq = "DTA_DATA_PACKAGE";
						break;
					case 32: // delivery info to ship
						if (list[i].data.quantity === 0 && list[i].data.targetQuantity > 0) eq = "DTA_DATA_DELIVERY";
						break;
					case 33:
						if (list[i].data.quantity === 1) {
							switch (list[i].data.stolenItemType) {
								case expandDescription("[gcm_stolen_item_types_1]"):
									eq = "EQ_GCM_STOLEN_SCHEMATICS";
									break;
								case expandDescription("[gcm_stolen_item_types_2]"):
									eq = "EQ_GCM_STOLEN_CODES";
									break;
								case expandDescription("[gcm_stolen_item_types_3]"):
									eq = "EQ_GCM_STOLEN_DOCUMENTS";
									break;
								case expandDescription("[gcm_stolen_item_types_4]"):
									eq = "EQ_GCM_WEAPONDESIGNS";
									break;
							}
						}
						break;
					case 50:
						if (p.equipmentStatus("EQ_GCM_MEDICAL_SUPPLIES") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_MEDICAL_SUPPLIES") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_MEDICAL_SUPPLIES";
						break;
					case 52:
						if (p.equipmentStatus("EQ_GCM_VIRUS_SPECIMENS") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_VIRUS_SPECIMENS") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_VIRUS_SPECIMENS";
						break;
					case 60:
					case 61:
						if (p.equipmentStatus("EQ_GCM_WBSA") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_WBSA") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_WBSA";
						break;
					case 62: // security software courtesy of GalCop
						if (list[i].data.quantity === 0 && list[i].data.targetQuantity > 0) eq = "DTA_GALCOP_SEC_SOFTWARE";
						break;
					case 65:
						if (p.equipmentStatus("EQ_GCM_SOFTWARE") === "EQUIPMENT_OK") "EQ_GCM_SOFTWARE";
						break;
					case 70:
					case 73:
						if (p.equipmentStatus("EQ_GCM_SOLAR_SCANNER") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_SOLAR_SCANNER") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_SOLAR_SCANNER";
						break;
					case 100:
						if (p.equipmentStatus("EQ_GCM_SEISMIC_SCANNER") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_GCM_SEISMIC_SCANNER") === "EQUIPMENT_DAMAGED") eq = "EQ_GCM_SEISMIC_SCANNER";
						break;
				}
				if (eq !== "") {
					//log(this.name, "found " + eq + " ... adding to BM list");
					sbm.$addSaleItem({
						key: eq + ":" + list[i].ID,
						text: this._sellInfo[eq].description,
						cost: parseInt(this._sellInfo[eq].cost * factor),
						worldScript: "GalCopBB_HitTeams",
						sellCallback: "$sellItemCallback"
					});
				}
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// handles the process of selling items on the black market (if Smugglers is installed)
this.$sellItemCallback = function $sellItemCallback(key, amount) {

	if (key.indexOf(":") === -1) {
		throw "!ERROR: Invalid key data returned from Black Market sell.";
	}
	var bb = worldScripts.BulletinBoardSystem;
	var gcm = worldScripts.GalCopBB_Missions;
	var typ = key.split(":")[0];
	var missID = parseInt(key.split(":")[1]);

	if (missID > 0) {
		var item = bb.$getItem(missID);
	}

	// remove the equipment from the player ship
	if (typ.indexOf("EQ_") != -1) {
		var eqsts = player.ship.equipmentStatus(typ);
		if (eqsts === "EQUIPMENT_OK" || eqsts === "EQUIPMENT_DAMAGED") {
			player.ship.removeEquipment(typ);
		}
	} else {
		// its something other than an equipment item
		if (typ.indexOf("DTA_") >= 0 && missID > 0) {
			// set the damaged element of the mission to 1-stop
			item.data.targetQuantity = 0;
		}
	}

	var missType = 0;
	var src = -1;
	var age = 0;
	if (missID > 0) {
		missType = item.data.missionType;
		src = item.data.source;
	} else {
		missType = gcm._equipmentFromFailedMissions[Math.abs(missID)].missionType;
		src = gcm._equipmentFromFailedMissions[Math.abs(missID)].source;
		// if this item is from a previous failed mission, extract the age from the key
		age = parseInt((clock.adjustedSeconds - gcm._equipmentFromFailedMissions[Math.abs(missID)].date) / 86400);
	}

	if (src >= 0) {
		// we'll be triggering the hit teams at this point
		this.$initiateInvestigation(src, missType, age);
	}

	// remove the equipment from the failed equipment list, if required
	if (missID < 0) {
		gcm._equipmentFromFailedMissions.splice(Math.abs(missID), 1);
	}
}

//-------------------------------------------------------------------------------------------------------------
// create a new "investigation" team, with the source system ID and mission type
this.$initiateInvestigation = function $initiateInvestigation(systemID, missionType, age) {

	var team = {};
	team.source = systemID;
	team.location = systemID;
	team.distance = 0;
	team.ships = [];

	var num_ships = 0;
	var period = 0;

	switch (missionType) {
		case 22:
		case 23: // black boxes
		case 24: // special cargo
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 15)) || Math.random() < 0.3) return;
			team.maxPursuitDistance = 50;
			num_ships = parseInt(Math.random() * 3 + 2);
			period = 120;
			break;
		case 31: // data cache collection
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 20)) || Math.random() < 0.2) return;
			team.maxPursuitDistance = 70;
			num_ships = parseInt(Math.random() * 3 + 2);
			period = 180;
			break;
		case 32: // delivery info to ship
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 20)) || Math.random() < 0.2) return;
			team.maxPursuitDistance = 70;
			num_ships = parseInt(Math.random() * 3 + 2);
			period = 180;
			break;
		case 42: // special computers
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 15)) || Math.random() < 0.3) return;
			team.maxPursuitDistance = 60;
			num_ships = parseInt(Math.random() * 3 + 2);
			period = 150;
			break;
		case 50: // medical equipment
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 90)) || Math.random() < 0.05) return;
			team.maxPursuitDistance = 0; // these guys will be relentless
			num_ships = parseInt(Math.random() * 4 + 4);
			period = 365;
			break;
		case 52: // virus specimins
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 90)) || Math.random() < 0.05) return;
			team.maxPursuitDistance = 0; // these guys will be relentless
			num_ships = parseInt(Math.random() * 4 + 4);
			period = 365;
			break;
		case 60: // 
		case 61: // wbsa devices
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 60)) || Math.random() < 0.08) return;
			team.maxPursuitDistance = 0; // these guys will be relentless
			num_ships = parseInt(Math.random() * 4 + 4);
			period = 365;
			break;
		case 62: // security software courtesy of GalCop
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 90)) || Math.random() < 0.05) return;
			team.maxPursuitDistance = 0; // these guys will be relentless
			num_ships = parseInt(Math.random() * 4 + 4);
			period = 365;
			break;
		case 65: // galcop station security software
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 90)) || Math.random() < 0.05) return;
			team.maxPursuitDistance = 0;
			num_ships = parseInt(Math.random() * 5 + 4);
			period = 365;
			break;
		case 70: // solar scanner
		case 73: // solar scanner
		case 100: // seismic scanner
			// possible there will not be a hit team
			if ((age > 0 && Math.random() < (age / 15)) || Math.random() < 0.3) return;
			team.maxPursuitDistance = 40;
			num_ships = parseInt(Math.random() * 2 + 2);
			period = 60;
			break;
	}

	var leader = true;
	for (var i = 1; i <= num_ships; i++) {
		// make the first ship in the array the leader
		team.ships.push(this.$createShipDef(leader));
		leader = false;
	}

	this._hitTeams.push(team);
	// add a mission suppression entry
	this._suppressMissions.push({
		system: systemID,
		date: clock.adjustedSeconds + (period * 86400)
	});
}

//-------------------------------------------------------------------------------------------------------------
this.$createShipDef = function $createShipDef(isLeader) {
	var def = {};
	var dta = null;
	do {
		var dta = this._preferredAssassinShips[Math.floor(Math.random() * this._preferredAssassinShips.length)];
		if (isLeader === true && dta.isLeader === false) dta = null;
	} while (dta == null);
	def.dataKey = dta.key;
	def.personality = Math.floor(Math.random() * 32000);
	var gcm = worldScripts.GalCopBB_Missions;
	if (gcm._rsnInstalled) {
		def.shipName = gcm.$getRandomShipName(null, "assassin-light");
	} else {
		def.shipName = "";
	}
	return def;
}

//-------------------------------------------------------------------------------------------------------------
// creates the hit teams at the witchpoint
this.$createHitTeams = function $createHitTeams(pos) {
	// put ship died event handlers on all ships so we can monitor how many of them the player kills
	// give most of the ships injectors and a large number of beam lasers
	for (var i = 0; i < this._createTeams.length; i++) {
		var team = this._hitTeams[this._createTeams[i]];
		// assume the first ship in the list is the leader
		var result = system.addShips("[" + team.ships[0].dataKey + "]", 1, pos, 0);
		var leader = null;
		if (result) var leader = result[0];
		if (!leader || leader == null) continue;
		leader.entityPersonality = team.ships[0].personality;
		leader.shipUniqueName = team.ships[0].shipName;
		leader.setScript("oolite-default-ship-script.js");
		leader.switchAI("gcm-assassinAI.js");

		leader.script._teamIndex = this._createTeams[i];
		leader.awardEquipment("EQ_FUEL_INJECTION");
		leader.awardEquipment("EQ_ECM");
		leader.awardEquipment("EQ_SHIELD_BOOSTER");
		// assassins don't respect escape pods and won't expect anyone
		// else to either.
		leader.removeEquipment("EQ_ESCAPE_POD");
		leader.fuel = 7;
		leader.forwardWeapon = "EQ_WEAPON_MILITARY_LASER";
		if (Math.random() > 0.5) {
			leader.aftWeapon = "EQ_WEAPON_BEAM_LASER";
		} else {
			leader.aftWeapon = "EQ_WEAPON_PULSE_LASER";
		}
		leader.accuracy = 4;
		leader.bounty = 0;
		// attach our shipDied event
		if (leader.script.shipDied && leader.script.$gcm_hitteamsovr_shipDied == null) leader.script.$gcm_hitteamsovr_shipDied = leader.script.shipDied;
		leader.script.shipDied = this.$gcm_hitteams_shipDied;

		// remove any escorts added automatically by the system
		if (leader.escorts) {
			for (var j = leader.escorts.length - 1; j >= 0; j--) leader.escorts[j].remove(true);
		}

		var grp = new ShipGroup("gcm-assassin-group-" + i, leader);
		leader.group = grp;

		for (var j = 1; j < team.ships.length; j++) {
			var shpspec = team.ships[j];
			result = system.addShips("[" + shpspec.dataKey + "]", 1, pos, 3E3);
			var member = null;
			if (result) member = result[0];
			if (!member || member == null) continue;
			member.entityPersonality = shpspec.personality;
			member.shipUniqueName = shpspec.shipName;
			member.setScript("oolite-default-ship-script.js");
			member.switchAI("gcm-assassinAI.js");

			member.script._teamIndex = this._createTeams[i];
			member.awardEquipment("EQ_FUEL_INJECTION");
			member.awardEquipment("EQ_ECM");
			if (Math.random() > 0.5) member.awardEquipment("EQ_SHIELD_BOOSTER");
			// assassins don't respect escape pods and won't expect anyone else to either.
			member.removeEquipment("EQ_ESCAPE_POD");
			member.bounty = 0;
			member.fuel = 5;
			if (Math.random() > 0.3) {
				member.forwardtWeapon = "EQ_WEAPON_BEAM_LASER";
				if (Math.random() > 0.5) {
					member.aftWeapon = "EQ_WEAPON_BEAM_LASER";
				} else {
					member.aftWeapon = "EQ_WEAPON_PULSE_LASER";
				}
			} else {
				member.forwardWeapon = "EQ_WEAPON_PULSE_LASER";
			}
			member.accuracy = Math.random() * 6 - 3; // between -3 and +3

			if (member.script.shipDied && member.script.$gcm_hitteamsovr_shipDied == null) member.script.$gcm_hitteamsovr_shipDied = member.script.shipDied;
			member.script.shipDied = this.$gcm_hitteams_shipDied;

			// remove any escorts added automatically by the system
			if (member.escorts) {
				for (var k = member.escorts.length - 1; k >= 0; k--) member.escorts[k].remove(true);
			}

			member.group = grp;
			grp.addShip(member);
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// populates an array with ship data keys for use by the populator routines
this.$getPreferredShipList = function $getPreferredShipList() {
	this._preferredAssassinShips.length = 0;
	var assassinRoles = ["assassin-light", "assassin-medium", "assassin-heavy"];

	for (var role = 0; role < assassinRoles.length; role++) {
		var shipkeys = Ship.keysForRole(assassinRoles[role]);
		for (var i = 0; i < shipkeys.length; i++) {
			//var shipspec = Ship.shipDataForKey(shipkeys[i]);
			this._preferredAssassinShips.push({
				key: shipkeys[i],
				isLeader: (role > 0 ? true : false)
			});
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// returns true if missions are suppressed in this system, otherwise false
this.$areMissionsSuppressed = function $areMissionsSuppressed(sysID) {
	for (var i = 0; i < this._suppressMissions.length; i++) {
		if (this._suppressMissions[i].system === sysID && this._suppressMissions[i].date > clock.adjustedSeconds) return true;
	}
	return false;
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_hitteams_shipDied = function $gcm_hitteams_shipDied(whom, why) {
	if (this.ship.script.$gcm_hitteamsovr_shipDied) this.ship.script.$gcm_hitteamsovr_shipDied(whom, why);
	var ht = worldScripts.GalCopBB_HitTeams;
	var idx = this.ship.script._teamIndex;
	for (var i = 0; i < ht._hitTeams[idx].ships.length; i++) {
		if (ht._hitTeams[idx].ships[i].dataKey === this.ship.dataKey && ht._hitTeams[idx].ships[i].personality == this.ship.entityPersonality) {
			ht._hitTeams[idx].ships.splice(i, 1);
			// if everyone is dead, remove the team entry
			if (ht._hitTeams[idx].ships.length === 0) ht._hitTeams.splice(idx, 1);
			break;
		}
	}
}