| Scripts/market_observer.js | "use strict";
this.name = "market_observer3";
this.author = "spara";
this.description = "Show extra market information";
this.startUp = function() {
	this.$basisLimit = 100 //current avg is weighted with this value
	//init price gatherer
	if (!missionVariables.marketObserver3) {
		this.$priceData = new Object();
	}
	else {
		this.$priceData = JSON.parse(missionVariables.marketObserver3);
	}
	
	//init purchase log
	if (!missionVariables.marketObserverBuyLog3) {
		this.$buyLog = new Object();
	}
	else {
		this.$buyLog = JSON.parse(missionVariables.marketObserverBuyLog3);
	}
	
	//init contracted goods
	if (!missionVariables.marketObserverContracted) {
		this.$contracted = new Object();
	}
	else {
		this.$contracted = JSON.parse(missionVariables.marketObserverContracted);
	}
}
this.startUpComplete = function() {
	//for compatibility with commodity markets oxp. it changes system market in market screen
	this.$sMarket = system.mainStation.market;
	//on start up, check for new commodities.
	var sMarket = this.$sMarket;
	for (var i in sMarket) {
		if (!this.$priceData[i]) {
			this.$updatePriceData();
			break;
		}
	}
}
this.playerWillSaveGame = function () {
	missionVariables.marketObserver3 = JSON.stringify(this.$priceData);
	missionVariables.marketObserverBuyLog3 = JSON.stringify(this.$buyLog);
	missionVariables.marketObserverContracted = JSON.stringify(this.$contracted);
}
//mark commodities as contracted to be noted when player buys or sells. contracted goods do not affect trader's rating.
this.playerEnteredContract = function(type, contract) {
	if (type === "cargo") {
		var commodity = contract.co_type;
		if (this.$contracted[commodity]) {
			this.$contracted[commodity] += contract.co_amount;
		}
		else this.$contracted[commodity] = contract.co_amount
	}
}
//release commodities for being marked as contracted.
this.playerCompletedContract = function(type, result, fee, contract) {
	if (type === "cargo") {
		var commodity = contract.co_type;
		if (this.$contracted[commodity]) {
			this.$contracted[commodity] -= contract.co_amount;
			if (this.$contracted[commodity] <= 0) {
				delete this.$contracted[commodity];
			}
		}
	}
}
//cancel contracts when entering new galaxy as core does
this.playerEnteredNewGalaxy = function(galaxyNumber) {
	this.$contracted = new Object();
}
//update system prices when exiting WS
this.shipExitedWitchspace = function() {
	if (system.mainStation) {
		this.$updatePriceData();
		this.$sMarket = system.mainStation.market;
	}
}
//update average
this.$updatePriceData = function() {
	var sMarket = this.$sMarket;
	for (var i in sMarket) {
		//update the average of a known commodity using a weighting from this.$basisLimit
		if (this.$priceData[i]) {
			var avgBasis = this.$basisLimit;
			this.$priceData[i].average = (avgBasis * this.$priceData[i].average + sMarket[i].price) / (avgBasis + 1);
		}
		//init a new commodity using average from commodity def
		else {
			//there's a rare possibility that the commodity has no price_average
			if (sMarket[i].price_average) {
				this.$priceData[i] = {
					average: sMarket[i].price_average
				}
			}
			else {
				this.$priceData[i] = {
					average: sMarket[i].price
				}
			}
		}
	}
}
//update purchase log
this.playerBoughtCargo = function(commodity, units, price) {
	//there's a contract on commodity
	if (this.$contracted[commodity]) {
		var prevQuantity = player.ship.manifest[commodity] - units;
		var replaced = 0;
		//in case we replacing contracted goods, don't note replaced units on log/rating			
		while (prevQuantity < this.$contracted[commodity] && units > 0) {
			prevQuantity += 1;
			units--;
			replaced++;
		}
		if (replaced > 0) {
			player.consoleMessage(replaced + " units of contracted goods replaced.");
		}
	}
	if (units > 0) {
		if (this.$buyLog[commodity]) {
			//handle buying the same commodity multiple times
			if (this.$buyLog[commodity][0][1] === price) {
				this.$buyLog[commodity][0][0] += units;
				this.$updateManifestExtras();
				return;
			}
		}
		else {
			this.$buyLog[commodity] = new Array();
		}
		this.$buyLog[commodity].unshift([units, price]);
		this.$updateManifestExtras();
	}
}
//update purchace log
this.playerSoldCargo = function(commodity, units, price) {
	if (!this.$buyLog[commodity]) return;
	var i, currentElement;
	var tab = "";
	var unitCount = units;
	var profit = 0;
	while(unitCount > 0 && this.$buyLog[commodity].length > 0) { //purge buys from log
		currentElement = this.$buyLog[commodity].shift();
		//There is more than enough purchases in one log entry
		if (currentElement[0] > unitCount) {
			profit += unitCount * (price - currentElement[1]);
			currentElement[0] -= unitCount;
			this.$buyLog[commodity].unshift(currentElement);
			unitCount = 0;
		}
		//There is enough or less purchases in log entry
		else {
			profit += currentElement[0] * (price - currentElement[1]);
			unitCount -= currentElement[0];
		}
	}
	if (this.$buyLog[commodity].length === 0)
		delete this.$buyLog[commodity];
	//show profit if profit != 0
	if (profit !== 0) {
		player.consoleMessage("Profit by trade: "+formatCredits(profit/10, true, true ));
		worldScripts["mo-traders_rating3"].$reportProfit(profit);
	}
	this.$updateManifestExtras();
}
this.shipDockedWithStation = function(station) {
	//this is here for compatibility with commodity markets oxp. it changes system market when docked at main station
	if (station.isMainStation) {
		this.$sMarket = system.mainStation.market;
	}
	//write off lost cargo from the profit when docking
	var profitLoss = 0;
	for (var i in this.$buyLog) {
		if (!manifest[i]) var unitsInCargo = 0;
		else var unitsInCargo = manifest[i];
		var done = false;
		while (!done) {
			var unitsInLog = 0;
			//calculate total number of units in log
			for (var j = 0; j < this.$buyLog[i].length; j++) {
				unitsInLog += this.$buyLog[i][j][0];
			}
			var difference = unitsInLog - unitsInCargo;
			//check if cargo is missing
			if (difference > 0) {
				//the last log entry has more than enough units to be removed, no need to remove entries
				if (this.$buyLog[i][this.$buyLog[i].length-1][0] > difference) {
					profitLoss += this.$buyLog[i][this.$buyLog[i].length-1][1] * difference;
					this.$buyLog[i][this.$buyLog[i].length-1][0] -= difference;
					done = true;
				}
				//the last log entry has enough or less than enough units to be removed, need to remove last entry
				else {
					profitLoss += this.$buyLog[i][this.$buyLog[i].length-1][1] * difference;
					this.$buyLog[i].pop();
				}
			}
			else {
				done = true;
				if (this.$buyLog[i].length === 0) delete this.$buyLog[i];
			}
		}
	}
	if (profitLoss > 0) {
		worldScripts["mo-traders_rating3"].$reportProfit(-1 * profitLoss);
		player.consoleMessage(formatCredits(profitLoss/10, true, true )+" worth of cargo is lost.");
	}
	this.$updateManifestExtras();
}
this.shipLaunchedFromStation = function(station) {
	for (var i in this.$buyLog) {//sort purchase log descending
		this.$buyLog[i].sort(function(a, b){return b[1] - a[1]});
	}
	this.$updateManifestExtras();
}
this.guiScreenWillChange = function(to, from) {
	if (to === "GUI_SCREEN_MARKET") this.$updateManifestExtras();
}
//tabbing function, prefixes pads to the string
//order true postfixes with pads
this._tabulate = function(string, width, order) {
	var hairSpace = String.fromCharCode(31);
	var hairSpaceLength = defaultFont.measureString(hairSpace);
	var currentLength = global.defaultFont.measureString(string);
	var padsNeeded = Math.floor((width - currentLength) / hairSpaceLength);
	if (padsNeeded >= 1)
		var pads = new Array(padsNeeded).join(hairSpace);
	else var pads = "";
	if (!order) return (pads + string);
	else return (string + pads);
}
this._percentDiff = function(avg, local, capacity) {
	if (capacity !== 0) {
		var percent = (local - avg) / avg * 100;
		if (percent < -0.05) {
			var percentText = percent.toFixed(1) + " %%";
		}
		else if (percent > 0.05) {
			var percentText = "+" + percent.toFixed(1) + " %%";
		}
		else var percentText = "0.0 %%";
	}
	//no point in calculating difference for 0 prices.
	else var percentText = "— %%";
	return percentText;
}
this._absDiff = function(avg, local, capacity) {
	if (capacity !== 0) {
		var abs = (local - avg)/10;
		if (abs < -0.05) {
			var absText = abs.toFixed(1);
		}
		else if (abs > 0.05) {
			var absText = "+" + abs.toFixed(1);
		}
		else var absText = "0.0";
	}
	//no point in calculating difference for 0 prices.
	else var absText = "—";
	return absText;
}
this.$updateManifestExtras = function() {
	
	//check what market is being shown
	if (player.ship.docked) {
		var sMarket = player.ship.dockedStation.market;
	}
	else {
		if (player.ship.target && player.ship.target.isStation) {
			var bcMarket = Ship.shipDataForKey(player.ship.target.dataKey)["market_broadcast"];
			if (!bcMarket) bcMarket = true;
			else {
				var trues = ["1", "true", "yes"];
				bcMarket = bcMarket.toLowerCase();
				if (trues.indexOf(bcMarket) !== -1) bcMarket = true;
				else bcMarket = false;
			}
			if (bcMarket) var sMarket = player.ship.target.market;
			else var sMarket = this.$sMarket;
		}
		else var sMarket = this.$sMarket;
	}
	
	var cm = worldScripts["commodity_markets"];
	
	for (var i in sMarket) {
		//first we'll need to clean our possible earlier additions from comment
		//there might be other content so we'll do what we can to preserve that.
		//we're assuming (foolhardy?) that we're the last entry.
		var cmt = manifest.comment(i);
		if (cmt.indexOf("Purchase breakdown") !== -1) {
			var sliceIndex = cmt.indexOf("Purchase breakdown") - 2;
			cmt = cmt.slice(0, sliceIndex);
			manifest.setComment(i, cmt);
		}
		
		//average column
		var line = this._tabulate(formatCredits(this.$priceData[i].average/10, true, false), 2.4);
		
		//differences colums
		if (!system.isInterstellarSpace) {
			line += this._tabulate(this._absDiff(this.$priceData[i].average, sMarket[i].price, sMarket[i].capacity), 3);
			line += this._tabulate(this._percentDiff(this.$priceData[i].average, sMarket[i].price, sMarket[i].capacity), 4.5);
		}
		var buyColWidth = 17.5;
		
		//add possible commodity markets column
		if (player.ship.docked && cm) {
			line += this._tabulate(formatCredits(cm.$commodities[i][1]/10, true, false), 5.3);
			buyColWidth -= 5.3;
		}
		
		//add puchase log column
		if (this.$buyLog[i]) {
			line += this._tabulate(this.$buyLog[i][0][0] + " × ", buyColWidth); 
			line += formatCredits(this.$buyLog[i][0][1]/10, true, false);
			//more that one entry in log
			if (this.$buyLog[i].length > 1) line += " >";
			
			//commodity details (f8-f8)
			// 6 entries per line
			cmt += "\n\nPurchase breakdown of held cargo: ";
			var j = 0;
			while (j < this.$buyLog[i].length) {
				cmt += "\n";
				for (var k = j; k < j + 6; k++) {
					if (k < this.$buyLog[i].length) {
						cmt += this._tabulate(this.$buyLog[i][k][0] + " × ", 2.6);
						cmt += this._tabulate(formatCredits(this.$buyLog[i][k][1]/10, true, false), 2.7, true);
					}
					else break;
				}
				j += 6;
			}
			manifest.setComment(i, cmt);
		}
		
		manifest.setShortComment(i, line);
	}
}
 | 
                
                    | Scripts/mo_traders_rating.js | "use strict";
this.name           = "mo-traders_rating3";
this.author         = "spara";
this.description    = "Gives the player a Trader's Rating based on profit made.";
//Public function for reporting profit
this.$reportProfit = function(profit) {
	this.$profit += profit;
	this.$setInstruction();
}
this.startUp = function() {
	if (!missionVariables.marketObserverProfit) this.$profit = 0;
	else this.$profit = missionVariables.marketObserverProfit;
	this.$setInstruction();
}
this.playerWillSaveGame = function () {
	missionVariables.marketObserverProfit = this.$profit;
}
this.$traderRank = function() { //return trader rank string
	var traderRatings =[0, 1500, 5000, 20000, 50000, 100000, 250000, 500000, 1000000];
	var i;
	var traderRating = -1;
	for (i = 0; i < 9; i++) {
		if (this.$profit/10 >= traderRatings[i])
			var traderRating = i;
	}
	if (traderRating != -1)
		return expandDescription("[mo_trade_rating_" + traderRating+"]");
	else
		return expandDescription("[mo_trade_rating_minus]");
}
//Trader rank for market hud
this.$setInstruction = function() { //set trader rank text to manifest
	var traderRank = "Trader's Rating: " + this.$traderRank() + " (" + formatCredits(this.$profit/10, true, true ) + ")";
	player.ship.setCustomHUDDial("marketObserverProfit", traderRank);
	mission.setInstructions(traderRank+".", this.name);
}
 |