| Scripts/miningcontracts.js |
"use strict";
this.name = "miningcontracts";
this.author = "Diagoras";
this.copyright = "2015 Maxim Savchenko";
this.licence = "Ms-PL";
this.description = "Excavate asteroids for reward with deadline conditions.";
this.MC_stationPrimRoles = {
"rockhermit": true,
"rockhermit-chaotic": true,
"rockhermit-pirate": false
}
this.MC_companyPatterns = expandDescription("[mc_company_patterns]").split("|");
this.startUp = function () {
this.MC_contracts = [];
if (missionVariables.MC_initialized) {
this.MC_system = missionVariables.MC_system;
this.MC_clock = missionVariables.MC_clock;
this.MC_debt = missionVariables.MC_debt;
this.MC_subscr = missionVariables.MC_subscr;
var s = missionVariables.MC_contracts;
if (s && s.length > 0) this.MC_contracts = JSON.parse(s);
} else {
this.MC_system = "";
this.MC_clock = 0;
this.MC_debt = 0;
this.MC_subscr = 0;
}
}
this.startUpComplete = function () {
this.MC_countAsteroids();
this.MC_setInstructions();
if (player.ship.docked) this.shipDockedWithStation(player.ship.dockedStation);
}
this.shipDockedWithStation = function (station) {
if (!this.MC_stationPrimRoles[station.primaryRole]) return;
if (this.MC_system != system.name) {
this.MC_system = system.name;
this.MC_clock = 0;
this.MC_contracts = [];
this.MC_debt = 0;
}
station.setInterface("MiningContracts-bm", {
title: expandDescription("[mc_contracts_title]"),
category: expandDescription("[interfaces-category-employment]"),
summary: expandDescription("[mc_contracts_summary]"),
callback: this.MC_interface.bind(this)
});
}
this.playerWillSaveGame = function (message) {
missionVariables.MC_initialized = 1;
missionVariables.MC_system = this.MC_system;
missionVariables.MC_clock = this.MC_clock;
missionVariables.MC_debt = this.MC_debt;
missionVariables.MC_subscr = this.MC_subscr;
missionVariables.MC_contracts = JSON.stringify(this.MC_contracts);
}
this.shipExitedWitchspace = function () {
// this.MC_markHermits();
this.MC_countAsteroids();
var number = this.MC_contracts.length;
var total = this.MC_debt;
for (var i = 0; i < number; i++) {
var contract = this.MC_contracts[i];
if (contract.signed) total += contract.amount * contract.penalty - contract.reward;
}
this.MC_contracts = [];
this.MC_debt = 0;
this.MC_clock = 0;
if (total > 0) {
player.ship.setBounty(player.bounty + 50, "contract obligations");
var targets = system.shipsWithPrimaryRole("buoy-witchpoint");
if (targets.length > 0) {
targets[0].commsMessage(
expandDescription("[mc_applied_bounty]", { system: this.MC_system, bounty: formatCredits(50, true, true) }), player.ship);
}
}
this.MC_setInstructions();
}
this.MC_countAsteroids = function () {
this.MC_icequota = system.countShipsWithRole("staer9_asteroid") / system.countShipsWithRole("asteroid");
}
this.MC_setInstructions = function () {
var instructions = [];
var number = this.MC_contracts.length;
for (var i = 0; i < number; i++) {
var contract = this.MC_contracts[i];
if (contract.signed == 1) {
var msg =
expandDescription("[mc_contract_signed]", { amount: contract.amount, commodity: displayNameForCommodity(contract.cargo), deadline: this.MC_clockStringToMinutes(contract.deadline) });
instructions.push(msg);
}
}
this.MC_active = instructions.length > 0;
if (this.MC_active) instructions.unshift(expandDescription("[mc_instructions_header]"));
else instructions = null;
mission.setInstructions(instructions);
}
this.MC_clockStringToMinutes = function (time) {
return clock.clockStringForTime(time).slice(4, 13);
}
this.MC_waitPrice = 5;
this.MC_interfaceNoGen = function () {
var debtMsg =
this.MC_debt > 0 ? expandDescription("[mc_debt]", { system: this.MC_system, debt: formatCredits(this.MC_debt, true, true) }) : "";
var number = this.MC_contracts.length;
var parameters = {
title: expandDescription("[mc_mission_title]"),
message:
expandDescription("[mc_first_line][mc_second_line][mc_third_line]") +
expandDescription("[mc_fax_machine]") +
expandDescription("[mc_clerk]") +
expandDescription("[mc_seventh_line]") + expandDescription("[mc_per_use]", { amount: formatCredits(this.MC_waitPrice, true, true) }) +
debtMsg,
choices: {
"WAIT": expandDescription("[mc_wait]", { price: formatCredits(this.MC_waitPrice, true, true) }),
"XXIT": expandDescription("[mc_exit]")
},
allowInterrupt: true
}
for (var i = 0; i < number; i++) {
var contract = this.MC_contracts[i];
var status = expandDescription("[mc_status_list]").split("|")[contract.signed];
parameters.choices["C" + i]
= expandDescription("[mc_option]", {
status: status,
amount: contract.amount,
commodity: displayNameForCommodity(contract.cargo),
deadline: this.MC_clockStringToMinutes(contract.deadline),
price: formatCredits(contract.reward, true, true)
});
}
if (this.MC_debt > 0) parameters.choices["DEBT"] = expandDescription("[mc_repay_debt]");
mission.runScreen(parameters, this.MC_contractDetails.bind(this));
}
this.MC_interface = function () {
this.MC_generateContracts();
this.MC_interfaceNoGen();
}
this.MC_contractDetails = function (choice) {
if (choice == null) return;
if (choice == "DEBT") {
if (this.MC_debt > player.credits) player.consoleMessage(expandDescription("[mc_not_enough_cash]"));
else {
player.commsMessage(expandDescription("[mc_paid]", { amount: formatCredits(this.MC_debt, true, true) }));
player.credits -= this.MC_debt;
this.MC_debt = 0;
}
this.MC_interface();
return;
}
if (choice == "WAIT") {
if (player.credits < this.MC_waitPrice) {
player.consoleMessage(expandDescription("[mc_not_enough_cash]"));
this.MC_interface();
return;
}
player.commsMessage(expandDescription("[mc_paid]", { amount: formatCredits(this.MC_waitPrice, true, true) }));
player.credits -= this.MC_waitPrice;
this.MC_waitForContract();
this.MC_interfaceNoGen();
return;
}
if (choice == "XXIT") return;
var cid = choice.replace("C", "").split(":");
var contract = this.MC_contracts[cid[0]];
var debt = this.MC_debt;
var header, descr, deliv, ps, id, option;
var mediator = expandDescription("[mc_mediator]", { system: this.MC_system });
var warning = expandDescription("[mc_warning]", { mediator: mediator });
var terms = expandDescription("[mc_terms]", { mediator: mediator, warning: warning });
function debtMsg(reward) {
return debt > 0 ?
expandDescription("[mc_unpaid_withholding]", { mediator: mediator, amount: formatCredits(Math.min(reward, debt), true, true) }) : "";
}
switch (contract.signed) {
case 0:
header = expandDescription("[mc_contract_signed_header]");
descr = expandDescription("[mc_contract_subject]");
deliv = contract.amount;
ps = terms;
id = "SIGN";
option = expandDescription("[mc_contract_option]");
break;
case 1:
descr = expandDescription("[mc_contract_delivered]");
deliv = (contract.gross - contract.amount) + "/" + contract.gross + " ";
if (contract.amount > 0) {
header = expandDescription("[mc_contract_operating]");
ps = terms;
id = "CARGO";
option = expandDescription("[mc_contract_dispatch]", { amount: Math.min(contract.amount, manifest[contract.cargo]) });
} else {
header = expandDescription("[mc_contract_completed]");
ps = expandDescription("[mc_contract_complete_details]", { reward: formatCredits(contract.reward, true, true), debt: debtMsg(contract.reward) })
id = "REWARD";
option = expandDescription("[mc_contract_collect]");
}
break;
case 2:
header = expandDescription("[mc_contract_terminated]");
descr = expandDescription("[mc_contract_delivered]");
deliv = (contract.gross - contract.amount) + "/" + contract.gross + " ";
var balance = contract.reward - contract.amount * contract.penalty;
if (balance < 0) {
ps = expandDescription("[mc_contract_failed_debt]", { mediator: mediator, debt: formatCredits(-balance, true, true), warning: warning });
id = "DEBT";
option = expandDescription("[mc_contract_failed_option]");
} else {
ps = expandDescription("[mc_contract_failed_reward]", { reward: formatCredits(balance, true, true), message: debtMsg(balance) });
id = "REWARD";
option = expandDescription("[mc_contract_collect]");
}
break;
}
var parameters = {
title: header,
message:
expandDescription("[mc_contract_description]", {
company: contract.company,
system: this.MC_system,
descr: descr,
amount: deliv,
commodity: displayNameForCommodity(contract.cargo),
deadline: clock.clockStringForTime(contract.deadline),
reward: formatCredits(contract.reward, true, true),
penalty: formatCredits(contract.penalty, true, true),
postscript: ps
}),
choices: {},
allowInterrupt: id != "REWARD" && id != "DEBT"
}
parameters.choices["0_" + id + ":" + cid[0]] = option;
var next = "2_NEXT:" + cid[0];
var prev = "3_PREV:" + cid[0];
if (parameters.allowInterrupt) {
if (id == "CARGO") parameters.choices["1_TERMINATE:" + cid[0]] = expandDescription("[mc_terminate]");
if (contract.signed == 0) {
parameters.choices[next] = expandDescription("[mc_contract_next_unsigned]");
parameters.choices[prev] = expandDescription("[mc_contract_prev_unsigned]");
} else if (contract.signed == 1) {
parameters.choices[next] = expandDescription("[mc_contract_next_unfinished]");
parameters.choices[prev] = expandDescription("[mc_contract_prev_unfinished]");
}
parameters.choices["4_BACK"] = expandDescription("[mc_back]");
if (id == "SIGN") parameters.initialChoicesKey = "4_BACK";
}
switch (cid[1]) {
case "N":
parameters.initialChoicesKey = next;
break;
case "P":
parameters.initialChoicesKey = prev;
break;
}
mission.runScreen(parameters, this.MC_contractAction.bind(this));
}
this.MC_contractAction = function (choice) {
if (choice == null) return;
var ch = choice.split(":");
switch (ch[0]) {
case "0_SIGN":
var idx = ch[1];
this.MC_contracts[idx].signed = 1;
this.MC_contracts[idx].gross = this.MC_contracts[idx].amount;
player.consoleMessage(expandDescription("[mc_signed]"));
this.MC_setInstructions();
this.MC_contractDetails("C" + idx);
break;
case "0_DEBT":
var idx = ch[1];
var contract = this.MC_contracts[idx];
var debt = contract.amount * contract.penalty - contract.reward;
this.MC_debt += debt;
this.MC_contracts.splice(idx, 1);
this.MC_interface();
break;
case "0_REWARD":
var idx = ch[1];
var contract = this.MC_contracts[idx];
var reward = contract.reward - contract.amount * contract.penalty;
var repayment = Math.min(reward, this.MC_debt);
reward -= repayment;
this.MC_debt -= repayment;
player.commsMessage(expandDescription("[mc_rewarded]", { reward: formatCredits(reward, true, true) }));
player.credits += reward;
this.MC_contracts.splice(idx, 1);
this.MC_setInstructions();
this.MC_interface();
break;
case "0_CARGO":
var idx = ch[1];
var contract = this.MC_contracts[idx];
var amount = Math.min(contract.amount, manifest[contract.cargo]);
if (amount > 0) {
player.commsMessage(expandDescription("[mc_dispatched]", { amount: amount, commodity: displayNameForCommodity(contract.cargo) }));
this.MC_contracts[idx].amount -= amount;
manifest[contract.cargo] -= amount;
this.MC_subscr = Math.max(this.MC_subscr, this.MC_floorScale(clock.seconds, 900)) + amount * 900;
this.MC_setInstructions();
}
else player.consoleMessage(expandDescription("[mc_none]", { commodity: displayNameForCommodity(contract.cargo) }));
this.MC_contractDetails("C" + idx);
break;
case "1_TERMINATE":
var idx = ch[1];
var contract = this.MC_contracts[idx];
var parameters = {
title: expandDescription("[mc_mission_title]"),
message: expandDescription("[mc_terminate_confirm]", { company: contract.company }),
choices: {},
allowInterrupt: true
}
parameters.choices["0_TERMYES:" + idx] = expandDescription("[mc_yes]");
parameters.choices["1_TERMNO:" + idx] = expandDescription("[mc_no]");
parameters.initialChoicesKey = "1_TERMNO:" + idx;
mission.runScreen(parameters, this.MC_contractAction.bind(this));
break;
case "0_TERMYES":
var idx = ch[1];
this.MC_contracts[idx].signed = 2;
player.consoleMessage(expandDescription("[mc_terminated]"));
this.MC_setInstructions();
this.MC_contractDetails("C" + idx);
break;
case "1_TERMNO":
var idx = ch[1];
this.MC_contractDetails("C" + idx);
break;
case "2_NEXT":
var idx = ch[1];
var signed = this.MC_contracts[idx].signed;
while (true) {
idx++;
if (idx >= this.MC_contracts.length) idx = 0;
if (this.MC_contracts[idx].signed == signed) break;
}
this.MC_contractDetails("C" + idx + ":N");
break;
case "3_PREV":
var idx = ch[1];
var signed = this.MC_contracts[idx].signed;
while (true) {
if (idx <= 0) idx = this.MC_contracts.length;
idx--;
if (this.MC_contracts[idx].signed == signed) break;
}
this.MC_contractDetails("C" + idx + ":P");
break;
case "4_BACK":
this.MC_interface();
break;
}
}
this.MC_randomElement = function (array) {
return array[Math.floor(Math.random() * array.length)];
}
this.MC_randomExponential = function () {
return -Math.log(1 - Math.random());
}
this.MC_randomNormals = function () {
var u = Math.sqrt(this.MC_randomExponential());
var v = 2 * Math.PI * Math.random();
return {
x: u * Math.cos(v),
y: u * Math.sin(v)
}
}
this.MC_ceilScale = function (value, scale) {
return scale * Math.ceil(value / scale);
}
this.MC_floorScale = function (value, scale) {
return scale * Math.floor(value / scale);
}
this.MC_companyName = function () {
return this.MC_randomElement(this.MC_companyPatterns).replace("?", randomName());
}
this.MC_randomCargo = function () {
return Math.random() < this.MC_icequota ? "alloys" : "minerals";
}
this.MC_logistic = function (x) {
return 1 / (1 + Math.exp(x));
}
this.MC_uniform = function (x, y) {
return x + (y - x) * Math.random();
}
this.MC_amountMu = 3;
this.MC_timeMu = 6;
this.MC_rateLambda = 4;
this.MC_generationRate = ((Math.exp(this.MC_amountMu) + 0.5) * Math.exp(this.MC_timeMu) + 450) / this.MC_rateLambda;
this.MC_createContract = function (time) {
var rn = this.MC_randomNormals();
rn.y += (system.government - 3.5) / 8;
var amount = Math.ceil(Math.exp(rn.x + this.MC_amountMu));
var deadline = this.MC_ceilScale(time + amount * Math.exp(rn.y / 2 + this.MC_timeMu), 900);
var expires = this.MC_uniform(time, deadline);
var cargo = this.MC_randomCargo();
var price = player.ship.dockedStation.market[cargo].price / 10;
var reward = this.MC_ceilScale(this.MC_floorScale(amount * price, 10) * this.MC_logistic(rn.y), 10);
var contract = {
company: this.MC_companyName(),
expires: expires,
deadline: deadline,
cargo: cargo,
amount: amount,
reward: reward,
penalty: price,
signed: 0
}
this.MC_contracts.push(contract);
}
this.MC_generateContracts = function () {
var prevClock = this.MC_clock;
var nextClock = clock.seconds;
var pregen = 96 * 3600;
if (nextClock - prevClock > pregen) prevClock = nextClock - pregen;
while (true) {
prevClock += this.MC_randomExponential() * this.MC_generationRate;
if (prevClock > nextClock) break;
this.MC_createContract(prevClock);
}
this.MC_clock = nextClock;
this.MC_expireContracts();
}
this.MC_waitForContract = function () {
this.MC_clock += this.MC_randomExponential() * this.MC_generationRate;
this.MC_createContract(this.MC_clock);
var time = this.MC_clock - clock.seconds;
if (time > 0) clock.addSeconds(time);
this.MC_expireContracts();
}
this.MC_expireContracts = function () {
var number = this.MC_contracts.length;
var updated = [];
for (var i = 0; i < number; i++) {
var contract = this.MC_contracts[i];
if (contract.signed == 1 && contract.deadline < clock.seconds) contract.signed = 2;
if (contract.signed || contract.expires > this.MC_clock) updated.push(contract);
}
this.MC_contracts = updated;
this.MC_setInstructions();
}
|