Back to Index Page generated: Apr 20, 2026, 4:52:51 AM

Expansion Partner Trader

Content

Warnings

  1. http://wiki.alioth.net/index.php/Partner%20Trader -> 404 Not Found
  2. Information URL mismatch between OXP Manifest and Expansion Manager string length at character position 0
  3. Wiki check failed: org.apache.http.conn.ConnectTimeoutException: Connect to wiki.alioth.net:443 [wiki.alioth.net/109.70.41.29] failed: Connection timed out

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Perhaps you are not interested in Guilds or Leagues; perhaps leading a multi-ship convoy leaves you cold, perhaps forever fighting palls. But perhaps you do have a partner in trade or in love, or in both... Partner Trader lets you travel with them and share your income and your Merchanter lives. Perhaps you are not interested in Guilds or Leagues; perhaps leading a multi-ship convoy leaves you cold, perhaps forever fighting palls. But perhaps you do have a partner in trade or in love, or in both... Partner Trader lets you travel with them and share your income and your Merchanter lives.
Identifier oolite.oxp.Reval.Partner_Trader oolite.oxp.Reval.Partner_Trader
Title Partner Trader Partner Trader
Category Mechanics Mechanics
Author Reval Reval
Version 1.1.1 1.1.1
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL n/a
Download URL https://wiki.alioth.net/img_auth.php/e/e0/Partner_Trader.oxz n/a
License CC-BY-NC-SA 4.0 CC-BY-NC-SA 4.0
File Size n/a
Upload date 1746523402

Documentation

Equipment

Name Visible Cost [deci-credits] Tech-Level
G.E.T. fast-docking facility no 50 1+
Partner Trader (Cobra III) yes 1000 1+
Annul Partner-trader relationship yes 0 1+

Ships

Name
Cobra IIIc

Models

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

Scripts

Path
Scripts/autodock_conditions.js
"use strict";

this.allowAwardEquipment = function(equipment, ship, context) {
	if (context != "scripted") return false;
	return true
}
Scripts/pt_equipment_conditions.js
"use strict";

this.allowAwardEquipment = function(equipment, ship, context) {
	//if (context != "scripted") return false;
	if (equipment == "EQ_ET_AUTODOCK") return true;
	// Convoy cancellation conditions
	if (equipment == "EQ_PT_COBRA_PARTNER_REM") 
		if (player.ship.equipmentStatus ("EQ_PT_COBRA_PARTNER") === "EQUIPMENT_OK")
			return true; else return false;

	return true;
}
Scripts/pt_script.js
"use strict";

this.name = "Partner Trader";
this.author = "Reval";
this.description = "Perhaps you are not interested in Guilds or Leagues; perhaps leading a multi-ship convoy leaves you cold. But perhaps you do have a partner in trade or in love, or in both... Partner Trader lets you travel with them and share your income and your Merchanter lives.";
this.license = "CC-BY-NC-SA 4.0";
this.version = "1.1.1";

/* 

Version 1.1.1 - a few minor bug-fixes

Version 1.1.0 - Many bug-fixes, plus improvements that include
	Joint Account deposits and withdrawals (with overdraft percentage taken).
	An F4 Interfaces page showing every system visited with partner, including frequency of visits.
	Improved docking behaviour for Cobra-partner.
	Cobra-Partner (joint funds allowing) will extend ship's cargo capacity for parity with Player-partner's.
	Collisions with Player-partner's ship are now impossible.
	
*/


this.playerBoughtEquipment = function(equipment, paid) {
	var pc = player.consoleMessage;
	if (equipment == "EQ_PT_COBRA_PARTNER") {		
		pc("Adding your Partner's Cobra III data to ship's computer.",9);
		this.$ptCompanionEnabled="yes";
		this.$ptHasCompanion = true;
		var sta = player.ship.dockedStation;
		this._ptPrepAcc(sta);
		this._ptPrepOpt(sta);
		this._ptPrepName(sta);
		this._ptPrepShip(sta);
		this._ptShowStatus();
		if (this.$ptPartnerGendered==="no")
			this._ptPrepGender(sta);
		pc("Please give ALL Partner's details on the F4 Interfaces!",9);
	} else
	// Partnership annulment, removal and refund
	if (equipment == "EQ_PT_COBRA_PARTNER_REM") {
		player.ship.removeEquipment("EQ_PT_COBRA_PARTNER");
		player.ship.removeEquipment("EQ_CT_COBRA_PARTNER_REM");
		pc("Anulling your Cobra III Partnership.",9);
		player.credits += this.$ptCost/2;
		pc("Amount refunded: "+ this.$ptCost/2,9);
	}
}



this.shipDockedWithStation = function(station) {
	var pc = player.consoleMessage;
	var lfn = this._ptGetPlayerFirstName();
	var pfn = this._ptGetPartnerFirstName();
	// Share earnings reciprocally
	if ((this.$ptHasCompanion) && (this.$ptNumCompanion>0) && (this.$ptShips!=null) && (!this.$ptLeaderPaidPartner) && (!this.$ptPartnerPaidMe)) {
		var fee = 0;
		for (var x=1; x<= this.$ptNumCompanion; x++)
			fee += this.$ptHandlingFee;
		
		// Leader pays Partner 50% of earnings per system
		if (!this.$ptLeaderPaidPartner) {
			var bonus = Math.floor(this.$ptLeaderEarnings*0.5);
			player.credits -= bonus;
			this.$ptLeaderBonus = bonus;
			// add it to Total bonuses paid since contract
			this.$ptLeaderTotal += bonus;
			pc(lfn+"'s half-share for "+pfn+": "+bonus+" cr",9);
		}
	
	// Partner pays leader 50% of trade earnings
		var percentpay = Math.floor(this.$ptPartnerTotal*0.5);
		if (percentpay>0) pc(pfn+"'s half-share for "+lfn+ ": "+percentpay+" cr",9);
		this.$ptPartnerEarnings += percentpay;
		player.credits += percentpay;
		this.$ptPartnerPercent = percentpay;
		// prevent repeat payments in same system
		this.$ptPartnerPaidMe = true;
		this.$ptLeaderPaidPartner = true;
	
		if (this.$ptHasCompanion) {
			// update Partnership's Joint Account Balance
			this.$ptJointBalance += (bonus + percentpay);
			// Take Overdraft 10% if Joint Balance in the red
			if (this.$ptJointBalance<0) {
				let posnum = Math.abs(this.$ptJointBalance);
				let overdr = (posnum * 0.1);
				player.credits -= overdr;
				pc("Galbank overdraft 10% taken: -"+overdr+" cr.",9);
			}
		}
	}
	if (this.$ptHasCompanion) {
		// prepare partner-related interfaces
		//this._ptPrepAcc(station);
		//this._ptPrepOpt(station);
		this._ptPrepName(station);
		this._ptPrepShip(station);
		if (this.$ptPartnerGendered==="no")
			this._ptPrepGender(station);
		if (this.$ptJointBalance>0)
			this._ptPrepWithdraw(station);
		if (player.credits>10000)
			this._ptPrepDeposit(station);
		// prepare the 'Systems Visited' interface page
		if (this.$ptSystemsVisited != null)
			this._ptPrepVisits(station);
		this._ptShowStatus();	
	}
		
}


this.shipWillLaunchFromStation = function(station) {
	// enable fast docking
	this._installAutoDock();
	// set up 'Partner Monitor' (fires once per second)
	if(!this.$ptTimer) this.$ptTimer=new Timer(this,this._ptMonitorPartner.bind(this),0,1.0);
	// set up a faster speed-monitor timer
	if(!this.$ptTimer2) this.$ptTimer2=new Timer(this,this._ptMonitorSpeed.bind(this),0,0.1);
}



this.shipLaunchedFromStation = function(stationLaunchedFrom) {
	var pc = player.commsMessage, ps = player.ship;
	// Is there a Partner?
	this.$ptHasCompanion = (player.ship.equipmentStatus ("EQ_PT_COBRA_PARTNER") === "EQUIPMENT_OK");
	// Partner undocks with player
	if ((this.$ptHasCompanion) && (this.$ptNumCompanion>0) && (this.$ptLaunchShip===null)) {
		this.$ptLaunchShip = system.addShips("pt_convoy", this.$ptNumCompanion, system.mainStation.position,0.2);
		var ships = this.$ptLaunchShip;
		// prevent collisions
		ships[0].addCollisionException(ps);
		ps.addCollisionException(ships[0]);
		pc("");
		ships[0].commsMessage("Ready for witch, "+this._ptGetPlayerFirstName()+"? Catch you on the far side!", ps);
		//pc(this.$ptPartnerFullName+" will meet you after Jump.",6);
	}
}



this.shipEnteredStationAegis = function(station) {
	var pc = player.commsMessage, ps = player.ship;
	this.$ptAegisAttained = true;
	if (this.$ptHasCompanion && this.$ptNumCompanion>0) {
		pc(this._ptGetPlayerFirstName()+", lovely run - see you on Station!",9);
	}
}



this.shipExitedWitchspace = function() {
	var pc = player.commsMessage, s=system.stations;
	var ps = player.ship;
	// reset docking variables for new system
	this.$ptAegisAttained = false;
	this.$ptDockSwitched = false;
	this.$ptPartnerPassedAegis = false;
	// make sure only one faux-partner appears on undock
	this.$ptLaunchShip = null;
	// are we monitoring? - yes, always with partner
	this.$ptMonitor = ((this.$ptHasCompanion) && (this.$ptNumCompanion>0));
	// start the Monitor timer, if already created
	if (this.$ptMonitor) {
		if (this.$ptTimer) this.$ptTimer.start();
		if (this.$ptTimer1) this.$ptTimer1.start();
	}
	// Rendezvous with Partner at or near witchpoint
	if (this.$ptHasCompanion && this.$ptNumCompanion>0) {
		var pfn = this._ptGetPartnerFirstName();
		this.$ptPartnerFirstName = pfn;
		pc(pfn+" is seeking you in "+this.$ptPartnerShipName+"...",6); pc("");
		// partner ship appears on scan
		this.$ptShips = system.addShips("pt_convoy", this.$ptNumCompanion, ps.position);
		var ships = this.$ptShips;
		// prevent collisions
		ships[0].addCollisionException(ps);
		ps.addCollisionException(ships[0]);
		// Partner wants cargo space parity if you have extended yours
		ships[0].cargoSpaceCapacity = this.$ptPartnerShipCapacity;
		ships[0].likelyCargo = ships[0].cargoSpaceCapacity - 1;
		var cdiff = player.ship.cargoSpaceCapacity - ships[0].cargoSpaceCapacity;
		// must avoid overdrawing the Joint Balance in upgrading!
		// so find an amount of TC, if any, that will not overdraw
		var price = cdiff*1000;
		if (price>this.$ptJointBalance) price=this.$ptJointBalance;
		var ugTC = Math.floor(price/1000); // how many TC can pt buy?
		price = ugTC*1000;
		if (ugTC>=10) {	
			ships[0].cargoSpaceCapacity = ships[0].cargoSpaceCapacity + ugTC;
			// update globally for Partner's increased ship capacity
			this.$ptPartnerShipCapacity = ships[0].cargoSpaceCapacity;
			ships[0].likelyCargo = ships[0].cargoSpaceCapacity - 1;
			pc(this.$ptPartnerShipName+" capacity extended with Goods Containers.",9);
			pc(this._ptGetPlayerFirstName() + ", I withdrew from our Account for it.",9);
			// debit joint account for cost of partner's goods containers
			this.$ptJointBalance -= price;
			pc("( "+price+" galactic credits )",9);
		} else pc(this._ptGetPlayerFirstName()+", I can't match your cargo space yet.",9);
		// Partner reports-in with applicable information
		var main = system.mainStation.name;
		this.$ptPartnerTotal = 0.0;
		// create a Group
		this.$ptGroup = new ShipGroup("COBRA Partner",player.ship);
		for(var x=0; x<ships.length; x++) {
			ships[x].setCargoType("SCARCE_GOODS");
			// Partner's total earnings for this system
			var holds = "";
			for(var y=0; y<ships[x].cargoList.length; y++) {
				var sc = ships[x].cargoList[y];
				this.$ptPartnerTotal += this._ptMarketPriceByName(main, sc.commodity, sc.quantity);
				holds += sc.displayName+"("+sc.quantity+") ";
			}
			// partner tells partner what she's got
			var csu = ships[x].cargoSpaceUsed;
			var pnam = this._ptGetPlayerFirstName();
			pc(""); pc(pnam+", I've got "+csu+" TC of all these goodies: "+holds,9);
			// Leader's total earnings this system
			this.$ptLeaderEarnings = 0.0;
			for(var m=0; m<manifest.list.length; m++) {
				var mc = manifest.list[m];
				this.$ptLeaderEarnings += this._ptMarketPriceByName(main, mc.commodity, mc.quantity);
			}
			// Assess and report total earnings for leader at Main Station 
			this.$ptLeaderEarnings = Math.floor(this.$ptLeaderEarnings);
			pc(""); 
			pc(player.name+"'s earnings this system: "+this.$ptLeaderEarnings+" cr",9);
			// add partner's ship to a Group
			this.$ptGroup.addShip(ships[x]);
		}
		// Assess and report total earnings for partner at Main Station
		this.$ptPartnerTotal = Math.floor(this.$ptPartnerTotal);
		pc(this.$ptPartnerFullName+"'s Earnings this system: "+this.$ptPartnerTotal+" cr",9);
		// Add this system to list of systems visited with partner
		this._ptAddSystemVisited(system.name);
		this._ptShowStatus();
	}
	// set paid to 'no'
	this.$ptRuns ++;
	this.$ptPartnerPaidMe = false;
	this.$ptLeaderPaidPartner = false;
}



this.shipWillDockWithStation = function(station) {
	// remove fast-docking equipment
	player.ship.removeEquipment("EQ_ET_AUTODOCK");
	// stop the Monitor timer
	if (this.$ptTimer) this.$ptTimer.stop();
	// stop the speed timer
	if (this.$ptTimer2) this.$ptTimer2.stop();
	// disable convoy monitor
	this.$ptMonitor = false;
	// nullify Group
	this.$ptGroup = null;
}


// let partner ship's max speed match player's J-drive speed
this._ptMonitorSpeed = function() {
	var pss = player.ship.speed;
	if (player.ship.torusEngaged) {
		if ((this.$ptShips!==null) && (this.$ptMonitor))
			if (pss>this.$ptShips[0].maxSpeed)
				this.$ptShips[0].maxSpeed = pss;
	}
}



this._ptMonitorPartner = function() {
var ps = player.ship;
var pc = player.consoleMessage;
var pcc = player.commsMessage; 
var psn = this.$ptPartnerShipName;
var spos = system.mainStation.position;
// time to Station at current speed, in minutes
var timetoStation = null;
// present distance to station
var distanceSta = null;
// Raw distance to Station	 
distanceSta = ps.position.distanceTo(spos);
// Time to station in minutes
timetoStation = Math.floor(( distanceSta / ps.speed) / 60);
// distance to Station in Lave km (Lk)
var distanceStaLk = Math.floor(distanceSta / 100);

// periodically, check distances to Partner's ship
if ((clock.secondsComponent === 1) && (this.$ptShips != null) && (this.$ptMonitor)) {
	// show time to main Station, at current speed
	if (timetoStation != null) pc("Time to Station: "+timetoStation+" minutes.",9);
	// show distance to main Station, at current speed
	if (distanceStaLk != null) pc("Station distance: "+distanceStaLk+" Lk.",9);
	pc(" "); // line-break
	for (var x = 0; x<this.$ptShips.length; x++) {
		if (this.$ptShips[x] != null) {
			try {
				var dist = Math.floor(ps.position.distanceTo(this.$ptShips[x].position)/100);
				var pnam=this._ptGetPlayerFirstName();
				if (dist>300) pc(psn+": "+pnam+", I am "+dist+" Lk from you - off scan.",4);
					else pc(psn+": "+pnam+", I am "+dist+" Lk from you - on scan.",4);
			} catch(err) {
				if (this._ptProbablyDocked(this.$ptShips[x]))
					pcc(psn+" clamped and ready for unloading.",9);
				else
					pcc(psn+" is not responding.",9);
			}
		}
	}
}
if ((clock.secondsComponent === 10) && (this.$ptShips != null) && (this.$ptMonitor)) {
	var pcm = player.consoleMessage;
	if (!this._ptProbablyDocked(this.$ptShips[0])) {
		var ds = Math.floor(this.$ptShips[0].desiredSpeed);
		if (ds>-1) pcm(psn+": My current speed is "+ds,9);
		// check ONCE if partner passed Station Aegis
		if (!this.$ptPartnerPassedAegis) 
			this.$ptPartnerPassedAegis = this.$ptShips[0].withinStationAegis;
	} else pcc("I'm docked, "+this._ptGetPlayerFirstName()+". Waiting.",9);
}
// note ship-position one second ago
this.$ptLastPos = ps.position;

if ((this.$ptShips!=null) && (distanceStaLk <= 150) && (!this.$ptDockSwitched))
	// switch Partner AI to 'dock'
	for (var x = 0; x<this.$ptShips.length; x++) {
		this.$ptShips[x].switchAI("dockingAI.plist");
		this.$ptDockSwitched = true;
		player.commsMessage("Let's dock 'em, "+this.$ptPartnerFirstName+"!",6);
	}

}



this.playerWillSaveGame = function(message) {
	missionVariables.ptNumCompanion = this.$ptNumCompanion;
	missionVariables.ptCompanionEnabled = this.$ptCompanionEnabled;
	missionVariables.ptRuns =this.$ptRuns;
	// Cumulative total earned from convoy
	missionVariables.ptPartnerEarnings = this.$ptPartnerEarnings;
	// Cumulative bonuses paid to convoy
	missionVariables.ptLeaderTotal = this.$ptLeaderTotal;
	// Joint Account Balance
	missionVariables.ptJointBalance = this.$ptJointBalance;
	// Partner's Full Name
	missionVariables.ptPartnerFullName = this.$ptPartnerFullName;
	// Partner's first name 
	missionVariables.ptPartnerFirstName = this.$ptPartnerFirstName;
	// Partner's last name
	missionVariables.ptPartnerLastName = this.$ptPartnerLastName;
	// Partner's Ship type
	missionVariables.ptPartnerShipType = this.$ptPartnerShipType;
	// Partner's Ship name
	missionVariables.ptPartnerShipName = this.$ptPartnerShipName;
	// hold capacity of partner's ship
	missionVariables.ptPartnerShipCapacity = this.$ptPartnerShipCapacity;
	// Partner's Gender
	missionVariables.ptPartnerGender = this.$ptPartnerGender;
	// Have we imparted Partner's Gender?
	missionVariables.ptPartnerGendered = this.$ptPartnerGendered;
	// Systems visited together
	missionVariables.ptSystemsVisited = JSON.stringify(this.$ptSystemsVisited);
	// Number of times each system was visited
	missionVariables.ptSystemsVisitedCount = JSON.stringify(this.$ptSystemsVisitedCount);
}


this.startUpComplete = function () {
	var pc = player.consoleMessage;
	if (missionVariables.ptNumCompanion != null) 
		this.$ptNumCompanion = missionVariables.ptNumCompanion;
	if (missionVariables.ptCompanionEnabled != null) 
		this.$ptCompanionEnabled = missionVariables.ptCompanionEnabled;
    // System-to-system runs w/partner
	if (missionVariables.ptRuns != null)
		this.$ptRuns = missionVariables.ptRuns;
	// Cumulative total earnings shared by partner
	if (missionVariables.ptPartnerEarnings != null)
		this.$ptPartnerEarnings = missionVariables.ptPartnerEarnings;
	// Cumulative earnings shared with partner
	if (missionVariables.ptLeaderTotal != null)
		this.$ptLeaderTotal = missionVariables.ptLeaderTotal;
	// Joint Account Balance
	if (missionVariables.ptJointBalance != null)
		this.$ptJointBalance = missionVariables.ptJointBalance;
	// Partner's full name
	if (missionVariables.ptPartnerFullName != null)
		this.$ptPartnerFullName = missionVariables.ptPartnerFullName;
	// Partner's first name 
	if (missionVariables.ptPartnerFirstName != null)
		this.$ptPartnerFirstName = missionVariables.ptPartnerFirstName;
	// Partner's last name
	if (missionVariables.ptPartnerLastName != null)
		this.$ptPartnerLastName = missionVariables.ptPartnerLastName;
	// Partner's Ship type
	if (missionVariables.ptPartnerShipType != null)
		this.$ptPartnerShipType = missionVariables.ptPartnerShipType;
	// Partner's Ship name
	if (missionVariables.ptPartnerShipName != null)
		this.$ptPartnerShipName = missionVariables.ptPartnerShipName;
	// Partner's Gender
	if (missionVariables.ptPartnerGender != null)
		this.$ptPartnerGender = missionVariables.ptPartnerGender;
	// register Joint Account Balance on start
	this.$ptJointBalance = this.$ptPartnerEarnings + this.$ptLeaderTotal;
	// hold capacity of partner's ship
	if (missionVariables.ptPartnerShipCapacity != null)
		this.$ptPartnerShipCapacity = missionVariables.ptPartnerShipCapacity;
	// have we imparted Partner's gender?
	if (missionVariables.ptPartnerGendered != null)
		this.$ptPartnerGendered = missionVariables.ptPartnerGendered;
	// Systems visited together
	this.$ptSystemsVisited = JSON.parse(missionVariables.ptSystemsVisited);
	// Number of times each system was visited
	this.$ptSystemsVisitedCount = JSON.parse(missionVariables.ptSystemsVisitedCount);
	// CHECK
	log(this.name, this.name+" startup complete.");
	// Partner PRESENT?
	this.$ptHasCompanion = (player.ship.equipmentStatus ("EQ_PT_COBRA_PARTNER") === "EQUIPMENT_OK");
	if (this.$ptHasCompanion) {
		// prepare partner-related interfaces
		var sta = player.ship.dockedStation;
		//this._ptPrepAcc(sta);
		//this._ptPrepOpt(sta);
		this._ptPrepName(sta);
		this._ptPrepShip(sta);
		this._ptShowStatus();
		if (this.$ptPartnerGendered==="no")
			this._ptPrepGender(sta);
		if (this.$ptJointBalance>0)
			this._ptPrepWithdraw(sta);
		if (player.credits>10000)
			this._ptPrepDeposit(sta);
		// get Partner's first and second name split
		this.$ptPartnerFirstName = this._ptGetPartnerFirstName();
		var pnam = this._ptGetPlayerFirstName();
		// prepare the 'Systems Visited' interface page
		if (this.$ptSystemsVisited != null)
			this._ptPrepVisits(sta);
		// Stationmaster informs that partner is waiting to leave dock
		player.commsMessage(pnam+", "+this.$ptPartnerFirstName+" is waiting at spacedock.",9);
	} else player.commsMessage("A Partner awaits you in the Equipment Section!",9);
}



this.startUp = function() {
    log(this.name, "Initialising OXP " + this.name);
	// declare & initialize globals
	// we start with no partner
	this.$ptHasCompanion = false;
	this.$ptCompanionEnabled = "no";
	// A Trader can only have one Partner
	this.$ptNumCompanion = 1;
	// Notary fee for Partner Agreement
	this.$ptCost = 100;
	// Total cumulative earnings shared by partner
	this.$ptPartnerEarnings = 0;
	this.$ptPartnerPaidMe = false;
	this.$ptLeaderPaidPartner = false;
	// Partner earnings this system
	this.$ptPartnerTotal = 0.0;
	// Leader earnings this system
	this.$ptLeaderEarnings = 0.0;
	// Leader total earnings since Partnering
	this.$ptLeaderTotal = 0.0;
	// amount shared with Partner this system
	this.$ptLeaderBonus = 0.0;
	// amount shared by Partner this system
	this.$ptPartnerPercent = 0.0;
	// Joint Account Balance
	this.$ptJointBalance = 0.0;
	// system-to-system runs w/partner
	this.$ptRuns = 0;
	// global array for ship group = partner
	this.$ptShips = null;
	// ship Group
	this.$ptGroup = null;
	// the faux partner that spawns on undock
	this.$ptLaunchShip = null;
	// Are we monitoring?
	this.$ptMonitor = false;
	// Partner's full name
	this.$ptPartnerFullName = "COBRA Partner";
	// Partner's first name 
	this.$ptPartnerFirstName = "COBRA";
	// Partner's last name
	this.$ptPartnerLastName = "Partner";
	// Partner's Ship type
	this.$ptPartnerShipType = "Cobra Mk III";
	// Partner's Ship name
	this.$ptPartnerShipName = "Snakebite";
	// Partner's Gender (pronoun 'she', 'he', or 'they')
	this.$ptPartnerGender = "they";
	// have you imparted their gender?
	this.$ptPartnerGendered = "no";
	// Initial hold capacity of Partner's ship
	this.$ptPartnerShipCapacity = 35;
	// Array to hold systems visited
	this.$ptSystemsVisited = new Array();
	// Array to hold number of times a system was visited
	this.$ptSystemsVisitedCount = new Array();

}


// Add this system to list of systems visited with partner.
// If already present, just increment number of visits
this._ptAddSystemVisited = function(sysname) {
	// is system name present?
	if ((this.$ptSystemsVisited != null) && (this.$ptSystemsVisitedCount != null)) {
		var present = this.$ptSystemsVisited.indexOf(sysname);
		// if 'undefined', add the system
		if (present === -1) {
			this.$ptSystemsVisited.push(sysname);
			this.$ptSystemsVisitedCount.push(1);
		// if already visited, increment count of visits
		} else this.$ptSystemsVisitedCount[present]++;
	} else { 
		this.$ptSystemsVisited = new Array();
		this.$ptSystemsVisited.push(sysname);
		this.$ptSystemsVisitedCount = new Array();
		this.$ptSystemsVisitedCount.push(1);
	}
}



this._ptProbablyDocked = function(ship) {
	var pd = false;
	var pa = player.ship.withinStationAegis;
	// this is set once in Monitor function
	var sa = this.$ptPartnerPassedAegis;
	
	pd = ((typeof ship.position === 'undefined') && (isNaN(ship.speed)) && (sa));
	
	return pd;
}



this._ptShipBasicData = function(ship) {
	var pc = player.commsMessage;
	
	if (typeof ship !== 'undefined') {
		pc(ship.name,9);
		pc(ship.shipClassName,9);
		pc(ship.shipUniqueName,9);
		pc(ship.primaryRole,9);
		pc('Alert condition '+ship.alertCondition,9);
	} else pc("Ship is 'undefined'.",9);
}



this._installAutoDock = function() {
	var p = player.ship;
	// give the partnered trader free fast-docking facilities at any station
	if (p.equipmentStatus("EQ_DOCK_COMP") != "EQUIPMENT_OK") {
			p.awardEquipment("EQ_ET_AUTODOCK");
	}
}



this._ptGetRandomInt = function(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}



this._ptGetPartnerFirstName = function() {

	var nam = this.$ptPartnerFullName.split(" ");
	return nam[0];
}


this._ptGetPlayerFirstName = function() {

	var nam = player.name.split(" ");
	return nam[0];
}	


	
// price of a good at a given station, in cr
this._ptMarketPrice = function(sta, good) {
	var price=0.0, mar=sta.market;
	price = mar[good].price/10;
	return price.toFixed(1);
}



// price of <quantity> <named> good, ie. "furs", at a <named> station, ie. "Coriolis Station"
this._ptMarketPriceByName = function(namesta, namegood, quant) {
	var price=0, s=system.stations;
		
	for (var i=0; i<s.length; i++)  {
		if (s[i].name === namesta) { 
			price = this._ptMarketPrice(s[i], namegood);
			break;
		}
	}
	return price*quant;
}	
	


// Partners' earnings and Account staus for F5 Missions screen

this._ptShowStatus = function() {
		
	var lbs = this.$ptLeaderBonus;
	var cps = this.$ptPartnerPercent;
	var pte = this.$ptPartnerEarnings;
	var ltc = this.$ptLeaderTotal;
	var bal = this.$ptJointBalance;
	
	var ln = this._ptGetPlayerFirstName();
	var pn = this._ptGetPartnerFirstName();
		
	var status_LBS = "Earnings shared with "+pn+": "+formatCredits(lbs,true,true);
	var status_CPS = "Earnings shared by "+pn+": "+formatCredits(cps,true,true);
	var status_PTE = pn+"'s cumulative Shares: "+formatCredits(pte,true,true);
	var status_LTC = ln+"'s cumulative Shares: "+formatCredits(ltc,true,true);
	var status_BAL = "Balance in Joint Account: "+formatCredits(bal,true,true);
	
	mission.setInstructions(["COBRA Partner:",status_LBS,status_CPS,status_PTE,status_LTC,status_BAL],this.name);
}


	
/* F4 Interfaces */


// create 'Partner Agreement' page 
this._ptPrepAcc = function(station) {
	station.setInterface("ptAcc",{
	title: "Your Informal Agreement with your Partner in Trade",
	category: "Partner Trader",
	summary: "Details of your Informal Agreement with your Partner in Trade.",
	callback: this._ptShowAcc.bind(this)
	});	
}


// show 'Partner Agreement' page
this._ptShowAcc = function() {
	var hfc = this.$ptHandlingCount;
	var mfc = this.$ptMaintenanceCount;
	var mfi = this.$ptMaintenanceInterval;
	var hft = this.$ptHandlingFeeTotal;
	var mft = this.$ptMaintenanceFeeTotal;
	var dft = this.$ptDismantlingFeeTotal;
	// Total income shared with Partner since agreement
	var lbc = this.$ptLeaderTotal;
		
	var parameters = new Object();
	
	parameters.title = "Informal Trade Partnership Agreement";
	
	parameters.message = "'Dear Partner, \n\n";
	
	if (this.$ptNumCompanion>0) 
		parameters.message +="'I have informally agreed to share half of my total Station Earnings with you, in the same way as you have informally agreed to share half of your total Station Earnings with me. To that end we have set up a Joint Access Account to hold our Balance.' \n\n"; 
	 else
		parameters.message +="You have no Partnership Agreement at this time. \n\n";
	
	parameters.message +="Partner's half-shares from you, total: "+lbc+" cr ("+hfc+") \n"; 
	parameters.message +="Your half-shares from Partner, total: "+this.$ptPartnerEarnings+" cr. \n\n";

	parameters.message += "Your Informal trading agreement with your Partner may be dissolved at any time unilaterally. \n";
	
	parameters.choicesKey = "ptAcknowlege";
	
	mission.runScreen(parameters, callback);	

	function callback(choice) {
		if (choice === "1_YES")  player.commsMessage("Your Partner thanks you for honoring them in this way.");
	}
}



// create 'Partner Options' page 
this._ptPrepOpt = function(station) {
	station.setInterface("ptOpt",{
	title: "Your Partner Options",
	category: "Partner Trader",
	summary: "Your informal Partnership brings you these choices in regard to your Companion in Trade.",
	callback: this._ptShowOpt.bind(this)
	});	
}


// show 'Partner Options' page on F4 Inteface
this._ptShowOpt = function() {
		
	var parameters = new Object();
	var pc = player.commsMessage;
	
	parameters.title = "Your Trade Partnership Options";
	
	parameters.message = "Dear Partner Trader, \n\n";
	
	if (this.$ptNumCompanion>0) 
		parameters.message +="Your Trading Partner is pleased to offer you the following options in regard to your partnership agreement. You are asked only this: How many Partners do you wish to have? \n\n"; 
	 else
		parameters.message +="You have no Partnership Agreement at this time. Please select NONE from below: \n\n";
		
	
	parameters.choicesKey = "ptOptions";
	
	mission.runScreen(parameters, callback);	

	function callback(choice) {
		if (choice === "0_NONE") this.$ptNumCompanion=0; else 
		if (choice === "1_ONE")  this.$ptNumCompanion=1; else
			
		if (this.$ptNumCompanion<1) { 
			this.$ptCompanionEnabled="no";
			pc("Your disappointed Partner notes your snub!"); 
		} else {
			this.$ptCompanionEnabled="yes";
			pc("Your Partner affectionately notes your wish to continue!");
		}
	}
}


// Entry of Partner's Full Name

this._ptPrepName = function(station) {
	station.setInterface("ptName",{
	title: "Your Partner's Name",
	category: "Partner Trader",
	summary: "Here you may enter your Trade Partner's full name into your ship's computer database for comms.",
	textEntry: true,
	callback: this._ptShowName.bind(this)
	});	
}


this._ptShowName = function() {
		
	var parameters = new Object();
	
	parameters.title = "Your Trade Partner's Name";
	
	parameters.message = "\n\n Please enter your Trade Partner's full name:  \n\n";
		
	parameters.textEntry = true;
	
	mission.runScreen(parameters, callback);	

	function callback(ptName) {
	
		if (ptName !== "") {
			this.$ptPartnerFullName = ptName;
			this.$ptPartnerFirstName = this._ptGetPartnerFirstName();
			player.commsMessage("Your Partner's name has been recorded as "+ptName,9);
		} else player.commsMessage("No entry made for Partner's full name.",9);
	}

}



// Entry of Partner's Ship Name

this._ptPrepShip = function(station) {
	station.setInterface("ptShip",{
	title: "Your Partner's Ship's Name",
	category: "Partner Trader",
	summary: "Here you may enter your Trade Partner's ship's name into your own ship's systems.",
	textEntry: true,
	callback: this._ptShowShip.bind(this)
	});	
}


this._ptShowShip = function() {
		
	var parameters = new Object();
	
	parameters.title = "Your Trade Partner's Ship's Name";
	
	parameters.message = "\n\n\n\n Please enter your Trade Partner's ship's name:  \n\n";
		
	parameters.textEntry = true;
	
	mission.runScreen(parameters, callback);	

	function callback(ptShip) {
	
		if (ptShip !== "") {
			this.$ptPartnerShipName = ptShip;
			player.commsMessage("Your Partner's ship has been recorded as "+ptShip,9);
		} else player.commsMessage("No entry made for Partner's ship name.",9);
	}

}


// Prepare Partner Gender request
this._ptPrepGender = function(station) {
	station.setInterface("ptGender",{
	title: "Your Partner's Gender",
	category: "Partner Trader",
	summary: "The Station Master requests to know whether your partner is female, male, bi-gendered, multi-gendered, or 'other'...",
	callback: this._ptShowGender.bind(this)
	});	
}


// Present the option to impart your Partner's gender
this._ptShowGender = function() {
	
	var parameters = new Object();
	
	parameters.title = "The "+this.$ptSystem+" Station";
	
	var pnam = this.$ptPartnerFirstName;
	
	parameters.message = "Profuse and embarrassed apologies, but the Station Master of "+this.$ptSystem+" has been unable to determine "+pnam+"'s  gender... Would you perhaps be willing to impart it to me, his assistant, so that no faux pas in future protocol will occur, nor any possible embarrassment on "+pnam+"'s part? \n\n ";
	
	parameters.message += "If female, we shall be pleased and honoured to call "+pnam+"  'Sister'; if male - 'Brother'; if bi-, tri-, or null-gendered - 'Sibling'... or simply continue with 'Merchant' if "+pnam+" prefers. \n ";
	
	parameters.choicesKey = "ptGenderChoices";
	
	mission.runScreen(parameters, callback);	

	function callback(choice) {
		if (choice === "1_FEMALE") this.$ptPartnerGender="she"; else
		if (choice === "2_MALE") this.$pPartnerGender="he"; else
		if ((choice === "3_BOTH") || (choice== "4_NULL")) this.$ptPartnerGender="them"; else
			this.$ptPartnerGender = "them";
		player.consoleMessage("Thank you. We will remember it.",7);
		this.$ptPartnerGendered="yes";
	}
}



// create 'Joint Account Withdrawal' page 
this._ptPrepWithdraw = function(station) {
	station.setInterface("ptWithdraw",{
	title: "Joint Account Withdrawal",
	category: "Partner Trader",
	summary: "Make a withdrawal from the GalBank account jointly held with your Trading Partner",
	callback: this._ptShowWithdraw.bind(this)
	});	
}


// show 'Joint Account Withdrawal' page
this._ptShowWithdraw = function() {
		
	var parameters = new Object();
	
	var pc = player.commsMessage;
	var sname = system.name;
	
	parameters.title = "GalBank "+sname+" Branch";
	
	parameters.message = sname+" Galbank cordially greets you! \n\n";
	
	parameters.message += "Commander, I see you have a withdrawal request. How many credits would you like to take from your Joint Account? \n\n";
	
	parameters.choicesKey = "ptWithdraw";
	
	mission.runScreen(parameters, callback);	

	function callback(choice) {
		var jb = this.$ptJointBalance;
		
		if (choice==="1_TEN") {this.$ptJointBalance-=10000; player.credits+=10000;} else
		if (choice==="2_TWENTY") {this.$ptJointBalance-=20000; player.credits+=20000;} else
		if (choice==="3_THIRTY") {this.$ptJointBalance-=30000; player.credits+=30000;} else
		if (choice==="4_FORTY") {this.$ptJointBalance-=40000; player.credits+=40000;} else
		if (choice==="5_FIFTY") {this.$ptJointBalance-=50000; player.credits+=50000;} else
		if (choice==="6_ALL") {this.$ptJointBalance-=jb; player.credits+=jb;}
		if (choice!=="0_NOTHING") {
		
			if (this.$ptJointBalance<0.0) { 
				pc("Interest will be charged on your overdraft.",9);
			} else {
				pc("Your ship has been credited with the withdrawal amount.",9);
			}
			this._ptShowStatus();	

			pc(sname+" Galbank thanks you for your custom!",9);
		}
	}
}




// create 'Joint Account Deposit' page 
this._ptPrepDeposit = function(station) {
	station.setInterface("ptDeposit",{
	title: "Joint Account Deposit",
	category: "Partner Trader",
	summary: "Make a deposit to the GalBank account jointly held with your Trading Partner",
	callback: this._ptShowDeposit.bind(this)
	});	
}


// show 'Joint Account Deposit' page
this._ptShowDeposit = function() {
		
	var parameters = new Object();
	
	var pc = player.commsMessage;
	var sname = system.name;
	
	parameters.title = "GalBank "+sname+" Branch";
	
	parameters.message = sname+" Galbank cordially greets you! \n\n";
	
	parameters.message += "Commander, I see you have a deposit to make. How many credits would you like to credit to your Joint Account? \n\n";
	
	parameters.choicesKey = "ptDeposit";
	
	mission.runScreen(parameters, callback);	

	function callback(choice) {
				
		if (choice==="1_TEN") {this.$ptJointBalance+=10000; player.credits-=10000;} else
		if (choice==="2_TWENTY") {this.$ptJointBalance+=20000; player.credits-=20000;} else
		if (choice==="3_THIRTY") {this.$ptJointBalance+=30000; player.credits-=30000;} else
		if (choice==="4_FORTY") {this.$ptJointBalance+=40000; player.credits-=40000;} else
		if (choice==="5_FIFTY") {this.$ptJointBalance+=50000; player.credits-=50000;}
		
		if (choice!=="0_NOTHING") {
			pc("Your Joint Account has been credited.",9);
			this._ptShowStatus();	
		}			
		pc(sname+" Galbank thanks you for your custom!",9);
	}
}



// create 'Systems Visited' page 
this._ptPrepVisits = function(station) {
	station.setInterface("ptVisits",{
	title: "Systems visited with "+this.$ptPartnerFirstName,
	category: "Partner Trader",
	summary: "View a list of the systems visited with your current partner.",
	callback: this._ptShowVisits.bind(this)
	});	
}


// show 'Systems Visited' page
this._ptShowVisits = function() {
		
	var parameters = new Object();
	
	var pc = player.commsMessage;
	
	parameters.title = "Systems visited with "+this.$ptPartnerFirstName;
	
	parameters.message = "Here are the systems you have both visited, listed in order of visit, along with the frequency of those visits: \n\n";
	
	// construct the systems-visited string
	var sv = this.$ptSystemsVisited;
	var svc = this.$ptSystemsVisitedCount;
	var svstr = "";
	if ((sv != null) && (svc != null)) {
		for (var x = 0; x<sv.length; x++) {
			svstr+=sv[x]+"(";
			svstr+=svc[x]+") ";
		}
	}
	
	parameters.message += svstr+ "\n";
	
	parameters.choicesKey = "ptAcknowledge";
	
	mission.runScreen(parameters, callback);	

	function callback(choice) {
		if (choice === "1_YES") 
			pc("More than welcome, "+this._ptGetPlayerFirstName()+".");
	}

}